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/mcpCursor
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
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 a project: one per brand, or one per product. Call after signup.
Create a wall and collect link
Create a testimonial wall AND its hosted collect page in one call. Call after create_project.
Ask customers for testimonials
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 real testimonials that already exist elsewhere (a tweet, a LinkedIn post, an email).
List testimonials
List testimonials newest first, 25 per page (max 100); pass next_cursor back to get the next page.
Approve a testimonial
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
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
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 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
Get a signed webhook call at a public https URL whenever a testimonial.submitted event happens on a project.
Get the payment link
Get the link where your human picks a plan and pays (Starter $19/month, Pro $39/month). Paying is the one step a human has to do.
List collect forms
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 another collect form for a brand: its own link, questions and settings (for a campaign or a product).
Change a collect form
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 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
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
Fix the author's details on a testimonial: name, role (title), company, picture (https URL) or email.
Fix a typo in a testimonial
Correct a typo in what the author wrote (spelling, punctuation, capitals).
Add or remove 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
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
Approve, reject (with a factual reason) or delete up to 100 testimonials you name by id.
List webhooks
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 a webhook and any deliveries still waiting to be retried. Get the webhook_id from list_webhooks.
Send a test event to a webhook
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
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
Undo an approve or a reject: the testimonial goes back to pending and leaves the wall at once.
See who was asked
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=..."
}| Code | HTTP | Meaning |
|---|---|---|
key_missing | 401 | No API key was sent. |
key_invalid | 401 | The key was not recognised. |
plan_required | 402 | The action needs the paid plan. |
limit_reached | 403 | A plan limit was reached. |
not_found | 404 | The project, wall or testimonial does not exist for this key. |
invalid_input | 400 | The input is wrong. The message says which field. A duplicate signup email is also invalid_input, with HTTP 409. |
signup_limited | 429 | Too many signups from this network. Wait retry_after seconds. |
upstream_failed | 502 | A service SocialPoof depends on failed. Retry in a moment. |
trial_used | 402 | A 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.