Webhooks
Trusty envia un webhook a tu endpoint cuando una verificacion termina (approved o rejected).
Como funciona
- Tu usuario completa el flujo en el widget (documento + selfie)
- Trusty procesa las imagenes con IA
- Cuando termina, Trusty envia un
POSTHTTP a la URL que configuraste - El webhook va firmado con HMAC-SHA256 para que puedas verificar que viene de Trusty
Evento
kyc.verification.completed
Header de firma
X-Trusty-Signature: sha256=<hmac>
El cuerpo del webhook se firma con HMAC-SHA256 usando tu webhook_secret. Siempre verifica la firma antes de procesar el payload.
Payload: verificacion aprobada
{
"event": "kyc.verification.completed",
"verification_id": "601e510f-b0c4-4f6d-a8a2-132fb9b40cf3",
"status": "approved",
"failure_reason": null,
"data": {
"document": {
"document_type": "national_id",
"issuing_country": "ESP",
"full_name": "GUILLERMO DANIEL CANELON LOVERA",
"document_number": "60518799A",
"date_of_birth": "1994-10-25",
"expiration_date": "2029-10-14",
"issue_date": "2024-10-14",
"readable": true,
"is_blurry": false,
"is_screen_recapture": false,
"is_photocopy": false,
"glare_detected": true
},
"liveness": {
"face_visible": true,
"single_face": true,
"eyes_visible": true,
"is_screen_recapture": false,
"is_printed_photo": false
},
"face_match": {
"same_person": true,
"confidence": "high"
}
},
"metadata": { "userId": "123" },
"completed_at": "2026-07-24T03:17:05.799Z"
}
Payload: verificacion rechazada
{
"event": "kyc.verification.completed",
"verification_id": "6ce8ef26-8a2b-48d9-ab97-638efe144137",
"status": "rejected",
"failure_reason": "document_unreadable,screen_recapture_detected",
"data": {
"document": {
"readable": false,
"is_blurry": true,
"is_screen_recapture": true,
"full_name": null,
"document_number": null
},
"liveness": { "face_visible": false },
"face_match": { "same_person": false, "confidence": "low" }
},
"metadata": null,
"completed_at": "2026-07-24T02:53:54.767Z"
}
Campos del payload
Nivel superior
| Campo | Descripcion |
|---|---|
event | Siempre kyc.verification.completed |
verification_id | UUID de la verificacion |
status | approved o rejected |
failure_reason | Si fue rechazada, los motivos separados por coma. Si fue aprobada, null |
data | Resultados del analisis (ver abajo) |
metadata | El JSON que tu enviaste en el widget, sin modificar |
completed_at | Timestamp ISO 8601 de cuando termino |
data.document
| Campo | Descripcion |
|---|---|
document_type | Tipo de documento: passport, national_id, drivers_license, other |
issuing_country | Pais emisor (codigo ISO 3 letras). null si no se pudo leer |
full_name | Nombre completo extraido del documento. null si no se pudo leer |
document_number | Numero de documento. null si no se pudo leer |
date_of_birth | Fecha de nacimiento (YYYY-MM-DD). null si no se pudo leer |
expiration_date | Fecha de expiracion (YYYY-MM-DD). null si no se pudo leer |
issue_date | Fecha de emision (YYYY-MM-DD). null si no se pudo leer |
readable | true si la IA pudo leer el documento correctamente |
is_blurry | true si el documento estaba borroso |
is_screen_recapture | true si se detecto que es una foto de una pantalla |
is_photocopy | true si se detecto que es una fotocopia |
glare_detected | true si se detectaron reflejos en el documento |
data.liveness
| Campo | Descripcion |
|---|---|
face_visible | true si se detecto un rostro en el video selfie |
single_face | true si solo habia una persona en el video |
eyes_visible | true si los ojos eran visibles |
is_screen_recapture | true si se detecto que el selfie es de una pantalla |
is_printed_photo | true si se detecto que el selfie es de una foto impresa |
data.face_match
| Campo | Descripcion |
|---|---|
same_person | true si el rostro del selfie coincide con la foto del documento |
confidence | Nivel de confianza: high, medium, low |
Motivos de rechazo (failure_reason)
Puede haber uno o varios separados por coma:
| Motivo | Descripcion |
|---|---|
document_unreadable | La IA no pudo leer el documento |
document_blurry | El documento estaba borroso |
screen_recapture_detected | Se detecto foto de pantalla en documento o selfie |
printed_photo_detected | Se detecto foto impresa en el selfie |
face_not_visible | No se detecto rostro en el selfie |
multiple_faces_detected | Habia mas de una persona en el selfie |
face_mismatch | El rostro del selfie no coincide con el documento |
birth_date_unreadable | No se pudo leer la fecha de nacimiento del documento |
Verificacion de firma
Node.js
import { createHmac, timingSafeEqual } from 'node:crypto'
const expected = createHmac('sha256', process.env.TRUSTY_WEBHOOK_SECRET)
.update(rawBody)
.digest('hex')
const valid = timingSafeEqual(
Buffer.from(signatureHeader),
Buffer.from(`sha256=${expected}`)
)
Python
import hmac, hashlib
expected = hmac.new(
webhook_secret.encode(),
raw_body,
hashlib.sha256
).hexdigest()
valid = hmac.compare_digest(signature_header, f"sha256={expected}")
PHP
$expected = hash_hmac('sha256', $rawBody, $webhookSecret);
$valid = hash_equals($signatureHeader, "sha256={$expected}");
Retries
- 3 intentos con backoff lineal (2s, 4s, 6s)
- 4xx (excepto 429): no reintenta
- 5xx, timeout, error de red: reintenta
- Timeout por intento: 10 segundos