API
API
Crea una sesión de verificación, envía al solicitante al enlace alojado, recibe el webhook firmado y consulta el resultado.
La referencia en formato máquina es GET /api/v1/openapi.json (OpenAPI 3.1, con webhooks); la legible es la Referencia de la API.
Tokens: real y de prueba
- Tokens personales. Un token actúa como su propietario en un equipo (
X-Team-Id), así que una integración deja de funcionar cuando ese usuario sale del equipo. Usa un usuario dedicado para integraciones en producción. - Permisos:
sessions:read,sessions:write,flows:read,webhooks:read(más los de cuenta,read/write). El rol del usuario en el equipo sigue aplicándose: borrar una sesión exige ser propietario o administrador. - Modo (se elige al crear, Cuenta, Tokens de API):
tid_live_...funciona solo sobre sesiones reales;tid_test_...solo sobre sesiones de prueba (livemode: false). El id de una sesión del otro modo da404. - Modo test: el enlace alojado muestra un aviso TEST y un simulador (aprobar, rechazar con un motivo, pedir un reenvío) en lugar de capturas reales.
Las sesiones de prueba nunca entran en la cola de revisión, no llegan a Pumble, no se facturan, no envían correos y el registro
se borra 24 horas después de su última actividad. Los webhooks se envían con normalidad con
"livemode": false.
Inicio rápido
# 1. Crea una sesión (con un token de prueba mientras integras)
curl -sS https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions \
-H "Authorization: Bearer $TRUSTID_TOKEN" \
-H "Idempotency-Key: pedido-1234-verificacion" \
-H "Content-Type: application/json" \
-d '{"flow_id":"01J...","external_reference":"pedido-1234","metadata":{"order":"1234"},"redirect_url":"https://tienda.example/verificado"}'
# -> 201 {"data":{"id":"01J...","status":"created","url":"https://YOUR-LEGITFLASH-DOMAIN/v/...","qr_svg_url":"...","embed":{...}}}
# 2. Redirige al solicitante a data.url (o muestra el QR, o incrústalo) y espera el webhook.
# 3. Consulta el resultado
curl -sS https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions/01J... -H "Authorization: Bearer $TRUSTID_TOKEN"
// Node 22
const res = await fetch('https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.TRUSTID_TOKEN}`,
'Content-Type': 'application/json',
'Idempotency-Key': `order-${order.id}-verification`,
},
body: JSON.stringify({ flow_id: process.env.TRUSTID_FLOW, external_reference: String(order.id) }),
});
const { data } = await res.json();
redirect(data.url); // solo se muestra en esta respuesta: guarda data.id, no el enlace
// PHP (cliente HTTP de Laravel)
$session = Http::withToken(config('services.trustid.token'))
->withHeaders(['Idempotency-Key' => "order-{$order->id}-verification"])
->post('https://YOUR-LEGITFLASH-DOMAIN/api/v1/sessions', [
'flow_id' => config('services.trustid.flow'),
'external_reference' => (string) $order->id,
])->throw()->json('data');
return redirect($session['url']);
Webhooks: ver Webhooks (firma, reintentos, carga; el cuerpo incluye también livemode y tus metadata).
Endpoints
| Método | Ruta | Permiso | Notas |
|---|---|---|---|
POST |
/sessions |
sessions:write |
201 + la sesión y url, qr_svg_url (firmada, 15 min), embed. Respeta Idempotency-Key |
GET |
/sessions |
sessions:read |
paginación por cursor; filtros status, flow_id, external_reference, created[gte|lte], decided[gte|lte], per_page (máx. 100) |
GET |
/sessions/{id} |
sessions:read |
estado, decisión, motivos, resultado (según el detalle del flujo), nombres de alertas, cronología, media_purge_at. Nunca archivos |
POST |
/sessions/{id}/cancel |
sessions:write |
solo antes del envío; si no, 409 not_cancellable |
POST |
/sessions/{id}/link |
sessions:write |
enlace nuevo, el anterior deja de valer; 409 not_relinkable una vez decidida o caducada |
DELETE |
/sessions/{id} |
sessions:write |
borrado ya a petición del interesado (archivos, registro, cargas de webhook), 204 |
GET |
/sessions/{id}/export |
sessions:read |
exportación para derecho de acceso (JSON, sin binarios, sin notas internas) |
GET |
/flows, /flows/{id} |
flows:read |
filtro archived=0|1 |
GET |
/usage |
sessions:read |
period, included, used, overage, cap (null = sin tope), overage_allowed |
GET |
/webhook-deliveries |
webhooks:read |
session_id, status; solo estado de la entrega, nunca cargas |
Cuerpo de creación: flow_id (obligatorio), external_reference, applicant.email, locale (en|es), send_invite (solo modo real),
redirect_url (su dominio debe ser uno de los de las URL de retorno del flujo), metadata (hasta 10 pares de texto, 500 bytes
cada uno, claves [A-Za-z0-9_.-]{1,40}). Los metadatos no deben contener nunca datos personales: se devuelven en respuestas y webhooks y se guardan en texto plano.
Idempotencia
Envía Idempotency-Key (máx. 255 caracteres) en cualquier POST. Por equipo y modo de token, durante 24 horas: la misma clave con la
misma petición repite la primera respuesta 2xx (cabecera Idempotent-Replayed: true, incluso la url de un solo uso); la misma clave con una petición
distinta da 422 idempotency_key_reused. Las peticiones fallidas no se guardan, así que un reintento corregido simplemente se ejecuta. Solo se guarda un hash de la clave
y la respuesta guardada va cifrada.
Errores, paginación, límites, versiones
- Los errores son RFC 9457
application/problem+jsoncon uncodeestable. Más en Errores. - Las listas usan paginación por cursor: sigue
links.nexthasta que seanull. - Límite de uso por token según el plan (
limits.api_rpm):X-RateLimit-Limit/Remaining,429+Retry-After. - Un token en la query string da
400; usa siempre la cabeceraAuthorization. - Versiones: los cambios dentro de
v1son aditivos (campos, endpoints, eventos y valores nuevos: ignora lo que no conozcas). Un cambio incompatible llegará como/api/v2con 12 meses de solapamiento, anunciado con las cabecerasDeprecationySunsetenv1. - Desarrolladores, API en el panel muestra las últimas 100 llamadas del equipo (método, ruta, estado, duración, nombre del token; sin cuerpos).