API de Testivora
Lee y administra tus testimonios, espacios y muros por código. Para agencias y clientes que quieren integrar Testivora en sus propios flujos y herramientas.
Autenticación
Cada request lleva una API key tipo Bearer, que creas en Ajustes → API. La llave es de tu cuenta: solo ve los datos de tu cuenta. Guardamos solo el hash de la llave — la verás completa una sola vez.
curl https://api.testivora.com/v1/account \
-H "Authorization: Bearer tv_live_xxxxxxxxxxxxxxxxxxxx"Las llaves tienen permisos: lectura (GET) y/o escritura (POST/PATCH). Una llave de solo lectura no puede crear ni modificar.
Errores
Todos los errores usan el mismo formato { error: { type, message } } con el código HTTP correcto:
401— falta el header o la llave es inválida/revocada402 quota_exceeded— alcanzaste el límite mensual de tu plan (incluye limit, used, tier)403— tu plan no incluye API, o la llave no tiene permiso de escritura404— el recurso no existe en tu cuenta422— error de validación (texto vacío, rating fuera de 1-5, etc.)429 rate_limited— demasiadas solicitudes (respeta el header Retry-After)
Rate limits
Por llave de API, independiente de tus quotas mensuales:
- Lecturas: 120/min
- Escrituras: 30/min
Son distintos de las quotas mensuales del tier (cuántos testimonios puedes crear al mes).
Paginación
Las listas usan cursor: la respuesta trae { object:"list", data, has_more, next_cursor }. Pasa ?cursor=<next_cursor>&limit=50 para la siguiente página. limit por default 25, máximo 100.
URLs de media
Las URLs de video, miniatura y foto son firmadas y expiran (~1 hora). Vuelve a pedir el recurso para refrescarlas; no las guardes a largo plazo. Las URLs externas que tú nos diste se devuelven tal cual.
Account
Tu cuenta: tier, acceso a la API, y uso vs. límites del plan.
/v1/accountDevuelve el objeto de la cuenta con uso del periodo actual y límites (null = ilimitado).
curl https://api.testivora.com/v1/account \
-H "Authorization: Bearer tv_live_xxxxxxxxxxxxxxxxxxxx"{
"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 }
}Spaces
Tus espacios (cada espacio es una colección de testimonios para un producto o servicio).
/v1/spacesLista todos tus espacios, del más reciente al más antiguo.
curl https://api.testivora.com/v1/spaces \
-H "Authorization: Bearer tv_live_..."{
"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
}/v1/spaces/{id}Devuelve un espacio por su id.
Testimonials
El núcleo de la API: leer, importar y administrar testimonios.
/v1/testimonialsLista testimonios dentro de un espacio (paginado por cursor). Filtros:
space_id— requerido. el espacio del que quieres listar.status— pending | approved | archived. Omítelo para traer todos.limit— default 25, máximo 100.cursor— el next_cursor de la página anterior.
curl "https://api.testivora.com/v1/testimonials?space_id=SPACE_ID&status=approved&limit=50" \
-H "Authorization: Bearer tv_live_..."{
"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"
}/v1/testimonials/{id}Devuelve un testimonio por su id.
/v1/testimonialsCrea un testimonio de TEXTO (importa uno que recolectaste por fuera). Cuenta contra tu quota mensual. Solo space_id y text son obligatorios.
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" }
}'/v1/testimonials/{id}Actualiza el status (pending/approved/archived), el flag featured y/o los tags.
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
Tus muros (walls). Un muro completo trae su configuración (template, theme, copy, blocks) MÁS sus testimonios aprobados ya filtrados y ordenados igual que el muro público — listos para renderizar donde quieras.
/v1/wallsLista los resúmenes de todos tus muros.
/v1/walls/{id} · /v1/walls?space_slug=Devuelve un muro completo por id, o el muro publicado de un espacio con ?space_slug=.
curl "https://api.testivora.com/v1/walls?space_slug=mi-curso" \
-H "Authorization: Bearer tv_live_..."Webhooks
Registra una URL en Ajustes → Webhooks y te mandamos un POST firmado cada vez que pasa algo. Perfecto para etiquetar en tu CRM, avisar en Slack o disparar un Zap/n8n.
testimonial.created— se recibió un testimonio nuevo (cualquier estado).testimonial.approved— un testimonio pasó a aprobado.testimonial.deleted— se eliminó un testimonio.
Cada entrega trae este sobre (el id es estable entre reintentos, úsalo para deduplicar):
{
"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) */ }
}Verifica que vino de nosotros: recalcula el HMAC-SHA256 del header Testivora-Signature con tu secret y compáralo en tiempo constante.
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),
);
}Crea tu primera API key desde tu panel y empieza a jalar tus testimonios. Disponible en Growth y Agency.
Crear mi API key →