Skip to content

SocialPoof docs

Everything you or your agent need to connect SocialPoof and run it. Agents can read the short version at /llms.txt.

What SocialPoof is

SocialPoof collects testimonials from your real customers and shows the ones you approve on your site. In the web app you make a collect form, send asks, read what comes back in your inbox, approve the good ones and embed a wall in one of four layouts. Customers can answer with text or a short video, and each wall is labelled “Selected by <brand>”.

An AI agent is one more way in, for people who use one. An agent, such as Claude Code or Cursor, is an assistant that can use tools for you. Connect it to SocialPoof and it can do what the web app does: sign up, set up forms and walls, send asks, approve, and return the embed code. It works in the same account you see when you sign in, so you can use the app and the agent side by side. Agent access is on every plan, trial included. This page covers that path.

It is free for 14 days with no card (1 brand, 25 text testimonials). After that, Starter is $19/month (1 brand, 1 form, text and video, no SocialPoof mark on your wall) and Pro is $39/month (5 brands, unlimited forms, and SocialPoof sends the ask emails). Running brands for clients? Talk to us about Agency.

Stars are shown with each testimonial by default, and you can switch them off for a wall. SocialPoof never shows an average or a review count. Emails and consent records are never public.

Connect your agent

SocialPoof is a remote MCP server over Streamable HTTP at https://socialpoof.io/mcp. There is nothing to install.

Claude Code

claude mcp add --transport http socialpoof https://socialpoof.io/mcp

Cursor

Add the server to ~/.cursor/mcp.json (or the project’s .cursor/mcp.json).

{
  "mcpServers": {
    "socialpoof": {
      "url": "https://socialpoof.io/mcp"
    }
  }
}

Any other MCP client

Point the client at https://socialpoof.io/mcp as a Streamable HTTP server. Tool discovery needs no key.

Keys

signup is the only tool that works without a key. It returns an API key once. Send it as Authorization: Bearer <key> on every other call, or pass it as the api_key argument. Keep it in user-local config, never in a committed file.

Then ask your agent: “Replace the testimonials on my homepage with real ones, using SocialPoof.”

The tools

29 tools, the same over MCP and REST. Descriptions here are the first lines of what the agent sees; the MCP server publishes the full text and every argument.

  • Sign up for SocialPoof

    signupPOST /api/v1/signupno key needed

    Create a SocialPoof account and get an API key. Call this FIRST when the user wants real customer testimonials on their site and you have no SocialPoof key yet.

  • Create a project

    create_projectPOST /api/v1/projects

    Create a project: one per brand, or one per product. Call after signup.

  • Create a wall and collect link

    create_wallPOST /api/v1/projects/:project_id/walls

    Create a testimonial wall AND its hosted collect page in one call. Call after create_project.

  • Ask customers for testimonials

    send_asksPOST /api/v1/asks

    Get a personal collect link and a ready-to-send draft message for each person you want to ask (up to 50).

  • Import existing testimonials

    import_testimonialsPOST /api/v1/imports

    Import real testimonials that already exist elsewhere (a tweet, a LinkedIn post, an email).

  • List testimonials

    list_testimonialsGET /api/v1/testimonialsread only

    List testimonials newest first, 25 per page (max 100); pass next_cursor back to get the next page.

  • Approve a testimonial

    approve_testimonialPOST /api/v1/testimonials/:testimonial_id/approve

    Approve a pending (or previously rejected) testimonial so it appears on the wall. Approve what the customer actually wrote; do not decide by star rating.

  • Reject a testimonial

    reject_testimonialPOST /api/v1/testimonials/:testimonial_id/reject

    Remove a testimonial from the wall (or keep a pending one off it). The reason must be factual and is never about sentiment: pii (personal data), profanity, off_topic, spam, or withdrawn (the author asked to be removed).

  • Show a shorter excerpt

    set_excerptPUT /api/v1/testimonials/:testimonial_id/excerpt

    Show only part of a long testimonial on the wall. The excerpt must be copied EXACTLY from the original text (an exact substring, same case and punctuation); anything edited is rejected with invalid_input.

  • Get the embed code for a wall

    get_embedGET /api/v1/embeds/:wall_idread only

    Get the code that puts the wall on the site: an HTML snippet (a keyless <socialpoof-wall> web component plus script), a React component for React/Next.js sites, the public JSON URL, the collect link and status_url.

  • Create a webhook

    create_webhookPOST /api/v1/webhooks

    Get a signed webhook call at a public https URL whenever a testimonial.submitted event happens on a project.

  • Share the install log

    share_install_runPOST /api/v1/install-run/share

    Publish the install log of this setup (the calls made, how long it took, and every error, exactly as recorded) so the owner can show it.

  • List collect forms

    list_formsGET /api/v1/formsread only

    List the collect forms (the pages people answer) with their link, open or closed state, questions, settings and how many responses each has.

  • Create a collect form

    create_formPOST /api/v1/forms

    Create another collect form for a brand: its own link, questions and settings (for a campaign or a product).

  • Change a collect form

    update_formPUT /api/v1/forms/:form_id

    Change a form's name, questions, which fields it asks for, star rating (on by default), text/video/both, brand colour and logo, thank-you message, or close it (closed:true) or reopen it (closed:false).

  • List walls

    list_wallsGET /api/v1/embedsread only

    List the walls (the displays you embed) with layout, whether stars show, min_items, tag filter, how many quotes each shows now and how many sites it was loaded on in the last 30 days.

  • Change a wall

    update_wallPUT /api/v1/embeds/:wall_id

    Change how a wall looks and what it shows: layout (grid, carousel, single, marquee), show_stars (each quote's own stars; never an average), min_items, filter_tags (only quotes with one of these tags), picked_only, or items: testimonial ids to pin first, in order (replaces the pick set; [] clears; up to 100).

  • Edit a testimonial's author details

    edit_testimonialPUT /api/v1/testimonials/:testimonial_id

    Fix the author's details on a testimonial: name, role (title), company, picture (https URL) or email.

  • Fix a typo in a testimonial

    fix_testimonial_textPUT /api/v1/testimonials/:testimonial_id/text

    Correct a typo in what the author wrote (spelling, punctuation, capitals).

  • Add or remove tags

    tag_testimonialPOST /api/v1/testimonials/:testimonial_id/tags

    Add and/or remove tags on a testimonial (up to 10 tags, 40 characters each). A wall can show only quotes with certain tags (update_wall filter_tags).

  • Delete a testimonial

    delete_testimonialDELETE /api/v1/testimonials/:testimonial_id

    Permanently delete a testimonial, its history and, for a video, the video itself (deleted at the video host; retried automatically if that fails).

  • Approve, reject or delete several testimonials

    bulk_testimonialsPOST /api/v1/testimonials/bulk

    Approve, reject (with a factual reason) or delete up to 100 testimonials you name by id.

  • List webhooks

    list_webhooksGET /api/v1/webhooksread only

    List the webhooks (public https URLs that receive testimonial.submitted events) with the last four characters of their signing secret and the status of their latest delivery.

  • Delete a webhook

    delete_webhookDELETE /api/v1/webhooks/:webhook_id

    Delete a webhook and any deliveries still waiting to be retried. Get the webhook_id from list_webhooks.

  • Send a test event to a webhook

    test_webhookPOST /api/v1/webhooks/:webhook_id/test

    Send one signed 'webhook.test' event to the webhook's URL now and report what happened: the HTTP status and the first 1 KB of the response body (response_body), or why it failed.

  • Get one testimonial

    get_testimonialGET /api/v1/testimonials/:testimonial_idread only

    Read one testimonial by id: the quote, author details (including the private email and rating), consent, answers to the form's extra questions, status, who approved it (approved_via) and when it last changed (updated_at).

  • Move a testimonial back to waiting

    unapprove_testimonialPOST /api/v1/testimonials/:testimonial_id/unapprove

    Undo an approve or a reject: the testimonial goes back to pending and leaves the wall at once.

  • See who was asked

    list_asksGET /api/v1/asksread only

    List the people asked for a testimonial, newest first, with their status: answered (a testimonial came back), waiting (no answer yet) or opted_out (they asked not to be emailed).

REST

Every tool is also a REST call under https://socialpoof.io/api/v1. The shorter /v1/... works too (for example POST /v1/signup). Send JSON bodies with Content-Type: application/json. GET calls take their arguments in the query string, never the key.

curl -X POST https://socialpoof.io/api/v1/signup \
  -H "Content-Type: application/json" \
  -d '{"email":"you@yourcompany.com"}'

# every other call sends the key it returned
curl -X POST https://socialpoof.io/api/v1/projects \
  -H "Authorization: Bearer sp_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Larkspur Notes","site_url":"https://larkspurnotes.com"}'

Each tool’s path is listed above. Errors use the same shape as MCP.

Errors

MCP and REST share one error body. Branch on code and follow hint. Over MCP the result has isError: true and this JSON as its text; over REST it is the response body, with the HTTP status below. signup_limited adds retry_after in seconds. plan_required, limit_reached and trial_used add upgrade_url: the link your human opens to pay.

{
  "code": "limit_reached",
  "message": "The trial allows 25 testimonials.",
  "hint": "A plan limit was reached. Call get_billing_link and ask your human to pay, or try again later.",
  "upgrade_url": "https://socialpoof.io/billing/start?token=..."
}
CodeHTTPMeaning
key_missing401No API key was sent.
key_invalid401The key was not recognised.
plan_required402The action needs the paid plan.
limit_reached403A plan limit was reached.
not_found404The project, wall or testimonial does not exist for this key.
invalid_input400The input is wrong. The message says which field. A duplicate signup email is also invalid_input, with HTTP 409.
signup_limited429Too many signups from this network. Wait retry_after seconds.
upstream_failed502A service SocialPoof depends on failed. Retry in a moment.
trial_used402A free trial already ran for this website.

Webhooks

create_webhook takes a project and a public https URL. SocialPoof then sends a POST with a testimonial.submitted event each time a customer submits. The call returns the signing secret (whsec_...).

{
  "id": "2f6c0d1e-9a41-4c7e-8e1b-5d3a7b9c0f12",
  "type": "testimonial.submitted",
  "created_at": "2026-10-17T14:02:11.000Z",
  "data": {
    "testimonial_id": "...",
    "wall_id": "...",
    "project_name": "Larkspur Notes",
    "status": "pending",
    "type": "text",
    "text": "The search actually finds the note I meant.",
    "rating": 5,
    "author": { "name": "Maya O.", "title": "Indie founder", "company": null, "avatar_url": null },
    "submitted_at": "2026-10-17T14:02:10.000Z"
  }
}

Each request carries SocialPoof-Signature: t=<unix>,v1=<hex>, where v1 is the HMAC-SHA256 of <t>.<raw body> using your secret. It also sends SocialPoof-Event and SocialPoof-Delivery. Check the signature against the raw body and reject old timestamps.

import { createHmac, timingSafeEqual } from "node:crypto";

// header: "t=1760709731,v1=9f2c..."  body: the raw request body, before JSON.parse
export function verify(secret, header, body) {
  const t = /t=(\d+)/.exec(header)?.[1];
  const v1 = /v1=([0-9a-f]+)/.exec(header)?.[1];
  if (!t || !v1) return false;
  if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // reject replays
  const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
  return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}

Answer with any 2xx within 5 seconds. A failure is retried after 1 minute, 10 minutes and 1 hour, then marked failed. Redirects are not followed, and URLs that resolve to private addresses are refused. The respondent’s email is never sent.

The embed

get_embed returns the snippet for a wall, a React component for React and Next.js sites, the public JSON address and the owner’s read-only status link. The HTML version:

<socialpoof-wall wall="wl_k3x9m2ab7qpd" data-count="5"></socialpoof-wall>
<script src="https://socialpoof.io/embed.js"></script>

The web component needs no key. It uses your site’s own font and colors, and shows nothing until the wall has its minimum number of approved quotes. data-count reserves the right height so the page does not jump. Free walls carry a small “Collected with SocialPoof” link; paid walls carry none.

Running brands for clients? Let’s talk.