Por que webhooks
Webhooks eliminam a necessidade de polling. Em vez de consultar o status repetidamente, o kycert notifica o seu servidor assim que o run é concluído. O endpoint recebe uma requisição POST com o resultado completo — você processa e responde em milissegundos.
Configuração
No dashboard kycert, vá em Settings → Webhooks e cadastre a URL do seu endpoint.
Alternativamente, passe webhook_url diretamente no POST /runs para sobrescrever o padrão por requisição.
Requisitos do endpoint
- HTTPS obrigatório em produção (HTTP aceito apenas em sandbox)
- Responder
2xx em até 30 segundos
- Idempotente — o mesmo evento pode ser entregue mais de uma vez
Eventos disponíveis
| Evento | Quando é disparado |
|---|
run.completed | Run finalizado com decisão (completed, blocked, pending_review, partial) |
run.failed | Falha técnica irrecuperável — run não produziu resultado |
Estrutura do payload
{
"id": "evt_01J4ZR...",
"object": "event",
"event": "run.completed",
"created": 1718200818,
"livemode": true,
"data": {
"object": "run",
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"decision": "approved",
"operative_decision": null,
"risk_band": "baixo",
"subject_type": "pf",
"template_id": "661e9511-f3ac-52e5-b827-557766551111",
"external_id": "cust_abc123",
"metadata": { "channel": "app_mobile" },
"created_at": "2026-06-12T14:00:00Z",
"completed_at": "2026-06-12T14:00:18Z"
}
}
Payload completo para run.failed
{
"id": "evt_9a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d",
"object": "event",
"event": "run.failed",
"created": 1781477806,
"livemode": true,
"data": {
"object": "run",
"id": "9ad7a680-0232-4da5-a3c5-934d65a87b1a",
"status": "blocked",
"decision": "rejected",
"operative_decision": "rejected",
"risk_band": "alto",
"subject_type": "pf",
"template_id": "f716ee22-3933-407b-9bf3-d82ab391ef94",
"external_id": null,
"metadata": {},
"checks_summary": {
"total": 19,
"valid": 0,
"invalid": 5,
"no_data": 0,
"error": 14
},
"created_at": "2026-06-14T22:56:44.091Z",
"completed_at": "2026-06-14T22:56:44.091Z"
}
}
run.failed usa status blocked quando o run foi bloqueado por regra crítica
(decision: "rejected"), ou failed quando houve falha técnica irrecuperável
(decision: null). Em ambos os casos, o formato do payload é idêntico.
Política de retry
Se o seu endpoint retornar um status fora de 2xx ou não responder em 30 segundos, o kycert tenta novamente com backoff exponencial:
| Tentativa | Delay |
|---|
| 1 | 5 min |
| 2 | 15 min |
| 3 | 30 min |
| 4 | 1h |
| 5 | 2h |
| 6 | 6h |
| 7 | 12h |
Após 7 tentativas sem sucesso (total ~24h), o evento é marcado como failed e nenhuma nova tentativa é realizada. O histórico de tentativas fica disponível no dashboard.
Nunca retorne 4xx por erros de lógica interna. Se o seu código falhar após receber um evento válido, responda 200 e trate o erro internamente. Um 4xx indica ao kycert que o evento foi rejeitado — e ele não tentará novamente.
Deduplicação
O campo id do evento é único. Armazene os IDs processados para ignorar duplicatas em caso de retry:
// Exemplo com Redis
const wasProcessed = await redis.get(`webhook:${event.id}`)
if (wasProcessed) return // já processado
await redis.setex(`webhook:${event.id}`, 86400, '1')
// processar evento
Verificação de assinatura
Todo webhook inclui o header kycert-signature. Sempre verifique — qualquer servidor pode fazer POST para o seu endpoint.
Consulte Segurança para o procedimento completo de verificação com exemplos em 3 linguagens.
Sandbox
Em sandbox, os webhooks são entregues normalmente. Use um serviço como webhook.site ou ngrok durante o desenvolvimento.
O campo livemode: false identifica eventos de sandbox.