testivora/API
API · v1

Testivora API

Read and manage your testimonials, spaces, and walls programmatically. For agencies and clients integrating Testivora into their own flows and tools.

llms.txt Interactive reference OpenAPI
Available on the Growth and Agency plans
Get started in 2 minutes
1) Create an API key in your dashboard → Settings → API. 2) Send it in the Authorization header. 3) Paste the “Copy for LLMs” button into ChatGPT or Claude and have it build the integration for you.
The basics

Authentication

Every request carries a Bearer API key, created in Settings → API. The key is account-scoped: it only sees your account's data. We store only the key's hash — you'll see it in full just once.

bash
curl https://api.testivora.com/v1/account \
  -H "Authorization: Bearer tv_live_xxxxxxxxxxxxxxxxxxxx"
Replace the token with your real key (starts with tv_live_).

Keys have scopes: read (GET) and/or write (POST/PATCH). A read-only key can't create or modify.

Errors

Every error uses the same { error: { type, message } } envelope with the right HTTP status:

  • 401 — missing header, or invalid/revoked key
  • 402 quota_exceeded — monthly plan quota reached (includes limit, used, tier)
  • 403 — your plan has no API access, or the key lacks write scope
  • 404 — resource not in your account
  • 422 — validation error (empty text, rating outside 1-5, etc.)
  • 429 rate_limited — too many requests (honor the Retry-After header)

Rate limits

Per API key, independent of your monthly quotas:

  • Reads: 120/min
  • Writes: 30/min

These differ from the tier's monthly quotas (how many testimonials you can create per month).

Pagination

Lists are cursor-paginated: the response carries { object:"list", data, has_more, next_cursor }. Pass ?cursor=<next_cursor>&limit=50 for the next page. limit defaults to 25, max 100.

Media URLs

Video, thumbnail, and photo URLs are signed and expire (~1 hour). Re-fetch the resource for fresh URLs; don't store them long-term. External URLs you provided are returned unchanged.

Endpoints

Account

Your account: tier, API access, and usage vs. plan limits.

GET/v1/account

Returns the account object with current-period usage and limits (null = unlimited).

bash
curl https://api.testivora.com/v1/account \
  -H "Authorization: Bearer tv_live_xxxxxxxxxxxxxxxxxxxx"
json
{
  "object": "account",
  "tier": "growth",
  "api_access": true,
  "period": "2026-06",
  "usage": { "total_testimonials": 240, "testimonials_this_period": 12 },
  "limits": { "testimonials_per_month": 500, "ai_credits_per_month": 3000 }
}
Example response.

Spaces

Your spaces (each space is a collection of testimonials for one product or service).

GET/v1/spaces

Lists all your spaces, most recent first.

bash
curl https://api.testivora.com/v1/spaces \
  -H "Authorization: Bearer tv_live_..."
json
{
  "object": "list",
  "data": [
    {
      "id": "k57...",
      "object": "space",
      "name": "Mi curso",
      "slug": "mi-curso",
      "primary_color": "#a8412c",
      "logo_url": "https://...signed...",
      "testimonial_count": 240,
      "approved_count": 180,
      "public_wall_url": "https://testivora.com/wall/mi-curso"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
Example response.
GET/v1/spaces/{id}

Returns one space by id.

Testimonials

The core of the API: read, import, and manage testimonials.

GET/v1/testimonials

Lists testimonials within a space (cursor-paginated). Filters:

  • space_id — required. the space to list from.
  • status — pending | approved | archived. Omit to return all.
  • limit — default 25, max 100.
  • cursor — the next_cursor from the previous page.
bash
curl "https://api.testivora.com/v1/testimonials?space_id=SPACE_ID&status=approved&limit=50" \
  -H "Authorization: Bearer tv_live_..."
json
{
  "id": "j97...",
  "object": "testimonial",
  "space_id": "k57...",
  "status": "approved",
  "type": "text",
  "text": "Cerré 3 clientes en una semana. Brutal.",
  "rating": 5,
  "featured": true,
  "tags": ["caso-b2b"],
  "author": { "name": "Ana López", "title": "Coach", "company": "Acme", "photo_url": null },
  "video": null,
  "images": [],
  "created_at": "2026-06-03T01:00:00.000Z",
  "published_at": "2026-06-03T01:00:00.000Z"
}
Each item in data has this shape.
GET/v1/testimonials/{id}

Returns one testimonial by id.

POST/v1/testimonials

Creates a TEXT testimonial (imports one collected elsewhere). Counts against your monthly quota. Only space_id and text are required.

bash
curl -X POST https://api.testivora.com/v1/testimonials \
  -H "Authorization: Bearer tv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "space_id": "SPACE_ID",
    "text": "Cerré 3 clientes en una semana. Brutal.",
    "rating": 5,
    "status": "approved",
    "tags": ["caso-b2b"],
    "author": { "name": "Ana López", "title": "Coach", "company": "Acme" }
  }'
Returns 201 with the created testimonial.
Text only in v1
Video testimonials can't be created via the API in v1 (they need an upload flow). Use the collector for video.
PATCH/v1/testimonials/{id}

Update status (pending/approved/archived), the featured flag, and/or tags.

bash
curl -X PATCH https://api.testivora.com/v1/testimonials/TESTIMONIAL_ID \
  -H "Authorization: Bearer tv_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "status": "approved", "featured": true }'

Walls

Your walls. A full wall returns its config (template, theme, copy, blocks) PLUS its approved testimonials already filtered and ordered exactly like the public wall — ready to render anywhere.

GET/v1/walls

Lists summaries of all your walls.

GET/v1/walls/{id} · /v1/walls?space_slug=

Returns a full wall by id, or a space's published wall via ?space_slug=.

bash
curl "https://api.testivora.com/v1/walls?space_slug=mi-curso" \
  -H "Authorization: Bearer tv_live_..."
Real-time events

Webhooks

Register a URL in Settings → Webhooks and we'll send a signed POST whenever something happens. Perfect for tagging in your CRM, pinging Slack, or triggering a Zap/n8n.

  • testimonial.created — a new testimonial was received (any status).
  • testimonial.approved — a testimonial moved to approved.
  • testimonial.deleted — a testimonial was deleted.

Every delivery carries this envelope (the id is stable across retries — use it to dedupe):

json
{
  "id": "evt_3hF2bQ...",
  "object": "event",
  "event": "testimonial.approved",
  "version": "1.0",
  "created": "2026-06-03T01:00:00.000Z",
  "attempt": 1,
  "data": { /* the testimonial object (same shape as the API) */ }
}

Verify it came from us: recompute the HMAC-SHA256 of the Testivora-Signature header with your secret and compare in constant time.

javascript
import crypto from "node:crypto";

// header = req.headers["testivora-signature"]  ->  "t=1717,v1=abc123…"
function verify(rawBody, header, secret) {
  const parts = Object.fromEntries(
    header.split(",").map((p) => p.split("=")),
  );
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${parts.t}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(parts.v1),
    Buffer.from(expected),
  );
}
Return 2xx fast; process async.
Retries & deliveries
If your endpoint doesn't return 2xx, we retry with exponential backoff (up to 6 attempts: 1m, 5m, 30m, 2h, 6h), then mark it failed. See the delivery log in Settings → Webhooks. After many consecutive failures we auto-pause the endpoint.
Ready to integrate?

Create your first API key from your dashboard and start pulling your testimonials. Available on Growth and Agency.

Create my API key →
© 2026 Testivora.
AutomationsEmbed guidesHome