Flujo: Webhooks (notificaciones asíncronas)
Para qué sirven
Trébol procesa documentos en background. Los webhooks te notifican cuando algo termina (item completado, verificación finalizada, CURP encontrada, etc.) sin que tengas que hacer polling.Crear un webhook
201:
secret solo se muestra una vez en esta respuesta. Guárdalo de inmediato en tu secret manager.
Tipos de eventos
verification.v2.created
Verificación creada en el sistema.
verification.v2.finished
Verificación completada exitosamente. Es el evento más importante — significa que ya puedes leer todos los resultados.
verification.v2.extraction_completed
Todos los items de extracción terminaron, pero la verificación aún no está marcada como “finished”. Útil para acceso anticipado a datos extraídos.
verification.v2.document_status_updated
Cambió el estado documental (documents_status) de la verificación: pending_upload → partial_upload → pending_external → full_upload (puede retroceder tras una reapertura). El payload incluye documents_status, previous_documents_status y updated_at. Para saber cuándo el prospecto completó su expediente, filtra por documents_status: "full_upload". Nota: este evento no incluye account_name.
verification.v2.findings.updated
Trébol recalculó los hallazgos de la Síntesis de Dictamen: lo que le falta al expediente o requiere atención. Se emite en cada corrida, incluidas las provisionales previas al finished. Es la primera vía por la que los hallazgos salen de Trébol. El payload trae findings[] (severity, message, missing_field?), source (provisional | final), computed_at y run_id. findings: [] es un resultado válido: la corrida no encontró nada. Nota: no trae account_name, ni status, ni verification_tag. status y el tag (como campo tag) los devuelve GET /verifications/{verification-id}; account_name no lo trae este evento ni ese endpoint (otros eventos v2 sí lo incluyen), así que tenlo de tu lado — account_id es tu propia cuenta. missing_field es una etiqueta descriptiva, no un catálogo cerrado, y no corresponde uno a uno con los item_type: muéstrala, no ramifiques lógica con ella. Solo se guarda la corrida más reciente, no un historial: si se agotan los reintentos, esa corrida no se recupera (consultar la verificación da la vigente, no la perdida). Persiste cada una solo si te importa la evolución; para el estado final basta leer la verificación.
verification_item.v2.completed
Un item específico completó su procesamiento. Contiene item_error si hubo problema. Códigos comunes:
- Documentales:
password_protected_pdf(PDF con contraseña),get_input_file_info_failed(falló al leer el archivo). doc_splitter:unsupported_file_type,unknown_custom_item_type,misconfigured_custom_item_type,no_splits_returned,pdf_slice_failed,pdf_slice_upload_failed,doc_splitter_request_failed(ver detalle en la guía dedoc_splitter).doc_validation:invalid_document_type,ruleset_validation_failed.
item_error como un string opaco: pueden llegar otros códigos específicos por item_type (por ejemplo prevalidation_failed en csf_mx).
verification_item.v2.internal_status_changed
Cambio de estado interno de un item.
verification_item.v2.extraction_completed
Item específico terminó su extracción (antes de marcarse completed). success: boolean indica si fue exitosa.
verification_people.curp_search_completed
Trébol terminó de buscar el CURP de una persona. Posibles errores:
curp_format_error— CURP mal formadocurp_scrapper_error— error al extraer info del servicio externocurp_service_unavailable— servicio caído
Ejemplos de payload por evento
verification.v2.finished
verification.v2.document_status_updated (expediente completo)
account_name.
verification.v2.findings.updated
findings: [] también es un payload válido: la corrida no encontró nada.
Forma distinta al GET: en el webhook findings es el array y source/computed_at van a su lado; en GET /verifications/{verification-id} es un objeto y los hallazgos están en findings.items. run_id existe solo en el webhook. source: "final" implica verificación completada.
Una corrida provisional se calcula antes de finalizar y corridas posteriores pueden reemplazarla. La final se calcula al finalizar y es la definitiva.
Para quedarte con la vigente, aplica la de computed_at más reciente y descarta las anteriores. Si empatan, final gana sobre provisional. Una verificación reabierta puede emitir dos final: ahí también decide computed_at. Mismo run_id = reentrega, no recálculo.
Disparador: se completa un item, con el expediente ya quieto y los hallazgos guardados viejos. Las corridas se agrupan (una por ráfaga, no una por documento). severity es enum cerrado (high|medium|low); missing_field no.
La corrida final se emite antes que verification.v2.finished, pero las entregas pueden llegar fuera de orden: si quieres los hallazgos definitivos al cierre, espera source: "final".
verification_item.v2.completed (con error)
item_error es opcional. Si el item se procesó bien, no aparece.
verification_people.curp_search_completed (con error)
Validar la firma HMAC-SHA256
Cada webhook llega con headerTrebol-Signature: t=1640995200,v1=abc123def456...
t=— timestamp Unix de cuándo se generó la firmav1=— firma HMAC-SHA256
req.headers['trebol-signature']). Si tu framework es case-sensitive, usa Trebol-Signature exactamente.
Pasos para validar
- Extraer
tyv1del header - Construir el string a firmar:
{timestamp}.{payload_raw} - Calcular HMAC-SHA256 usando tu webhook secret
- Comparar con
v1usando comparación de tiempo constante (no===)
Ejemplo Node.js
Ejemplo Python
IPs de origen
Trébol envía webhooks desde:35.170.236.12354.162.134.233
Reintentos
Si tu endpoint no responde2xx, Trébol reintenta con backoff exponencial:
Después de 5 fallos, el webhook se marca como fallido y no se reintenta más.
Trébol reintenta cuando:
- Tu servidor responde
4xxo5xx - Hay timeout de conexión
- Error de red
Reglas de oro
1. Responde 200 OK antes de procesar
Valida la firma, encola el evento, responde. El procesamiento real va en un worker.
2. Implementa idempotencia con la firma del header
Los reintentos pueden duplicar eventos. Usa la firmav1= del header Trebol-Signature como clave de deduplicación, ya que es única por evento. Esto es lo que recomienda la guía oficial de webhooks.
3. No asumas orden de eventos
Puedes recibirverification_item.v2.completed antes de verification.v2.created. Si necesitas estado actual, consulta el API.
4. Procesa con cola asíncrona
Usa RabbitMQ, SQS, Celery, BullMQ. Si haces todo síncrono, vas a tener timeouts en picos de tráfico.5. TTL de dedupe ≥ 24 h
Guarda los IDs procesados al menos 24 horas para manejar reintentos tardíos. Redis es ideal.6. Verifica el timestamp para evitar replay (opcional)
Rechaza webhooks cont= mayor a 5 minutos en el pasado:
Después de verification_people.curp_search_completed
Cuando recibas este webhook, puedes obtener los datos de CURP:
{verification-id} en kebab. El field verification_id (snake) está en el payload del webhook, lo conviertes en path al hacer la lectura.
En la respuesta, busca la persona cuyo people_id coincida y lee external_identities.curp: