Pular para o conteúdo principal

Visão geral

Esta página cobre a configuração do endpoint e o tratamento da requisição. Para o contrato do payload, veja Tipos de evento; para garantias de entrega, veja Reentregas.

Por que webhooks em vez de polling

AspectoPolling GET /v1/verify/{id}Webhooks
Latência do veredicto até o seu códigoSeu intervalo de pollingUma requisição, assim que o veredicto existe
Chamadas de API por verificação3–301 de entrada
Chave necessáriaChave secreta, somente no servidorNenhuma — em vez disso você verifica uma assinatura
Decisões de revisão humanaVocê precisa continuar fazendo polling indefinidamenteEntregues como um segundo evento
Se o seu serviço estiver fora do arVocê não perde nada, mas precisa continuar fazendo pollingReentregue por ~12,6 minutos, depois pode ser recolocado na fila

A última linha é a que mais importa. Um veredicto review é final até que uma pessoa o decida, o que pode levar horas. Fazer polling por ele significa ou fazer polling para sempre, ou desistir e deixar o usuário travado. O webhook da decisão do revisor chega quando chegar.

Configuração

Existe um webhook por tenant, configurado no painel em Settings → Webhook. Não há lista de endpoints, não há assinatura por evento e não há separação test/live para webhooks.

O formulário tem dois campos:

CampoRegras
URLPrecisa começar com https://. Caso contrário, é rejeitada antes de salvar. Sujeita também a uma proteção contra SSRF.
SecretVocê escolhe. Mínimo de 24 caracteres. Deixe o campo em branco para manter o segredo atual.

O segredo não é gerado para você e nunca é exibido de volta — o campo é somente escrita. Gere um com entropia real e guarde-o onde sua aplicação possa lê-lo:

openssl rand -hex 32

Uma vez salvo, a próxima verificação que chegar a um veredicto fará POST na sua URL.

Trocar o segredo tem efeito imediato

Salvar um novo segredo substitui o antigo na hora. Não existe janela de tolerância com dois segredos. Publique o novo segredo na sua aplicação primeiro, e só então salve-o no painel — veja rotação.

A requisição

POST /webhooks/veridia HTTP/1.1
Host: yourapp.com
Content-Type: application/json
Veridia-Signature: t=1753142348,v1=4f8a3b9c01ee5d2f...
Veridia-Event: verification.approved
Veridia-Event-Id: evt_9f2c41ab7d8e4c05b6a3e17f2d904c8b

{"id":"evt_9f2c41ab7d8e4c05b6a3e17f2d904c8b","type":"verification.approved", ...}
CabeçalhoUso
Veridia-Signaturet=<segundos unix>,v1=<hmac-sha256 em hex>. Verifique isto antes de confiar em qualquer coisa.
Veridia-EventO tipo do evento, espelhando o type do corpo. Conveniente para roteamento/métricas; não é autenticado por si só — o que o HMAC cobre é o corpo.
Veridia-Event-IdO mesmo valor do id do corpo. Permite consultar seu armazenamento de deduplicação antes de fazer o parse do corpo.

A assinatura cobre os bytes brutos do corpo. Não re-serialize o JSON antes de calcular o HMAC — a ordem das chaves e os espaços em branco fazem parte do que foi assinado. Veja Verificação de assinatura.

Responda 2xx em até 10 segundos

O timeout de entrega é de 10 segundos (5 segundos para conectar). Qualquer coisa mais lenta conta como tentativa falha e é reentregue, o que significa que seu handler lento-mas-bem-sucedido será chamado a fazer o mesmo trabalho de novo.

Confirme primeiro, trabalhe depois:

app.post('/webhooks/veridia', express.raw({ type: 'application/json' }), (req, res) => {
// 1. Verifique a assinatura — rápido, e a única coisa que precisa acontecer inline.
if (!verifyVeridiaSignature(req.header('Veridia-Signature'), req.body, SECRET)) {
return res.status(401).send('invalid signature');
}

// 2. Confirme. Tudo daqui para frente é no seu próprio tempo.
res.status(200).send('ok');

// 3. Faça o trabalho. Falhas aqui são sua responsabilidade reprocessar — a
// entrega já foi confirmada e não será reenviada.
const payload = JSON.parse(req.body.toString('utf8'));
enqueue(payload).catch(err => logger.error({ err, eventId: payload.id }));
});

Esse último comentário é o trade-off que você está aceitando. Confirmar cedo significa que uma queda entre o passo 2 e o passo 3 perde o evento, então persista o payload de forma durável (uma linha, uma mensagem em fila) como a confirmação, e faça o processamento real a partir daí. Os exemplos seguem todos esse padrão.

Códigos de status sobre os quais agimos

Sua respostaO que acontece
2xxEntregue. Pronto.
5xx, timeout, erro de conexãoReentregue conforme o cronograma de backoff
408, 429Reentregue
Qualquer outro 4xxFalha permanente. Não é reentregue — um 401 ou 404 significa que repetir a requisição idêntica não pode dar certo.

Uma falha permanente ainda aparece no painel e pode ser recolocada na fila manualmente depois que você corrigir a causa.

status não é verdict

Isto se aplica à API de polling, e não aos webhooks, mas é o erro mais caro que a API deste produto torna possível, e handlers de webhook o herdam sempre que fazem verificação cruzada com GET /v1/verify/{id}:

  • status é o estado do pipeline: queued, processing, completed, failed.
  • verdict é o resultado: approved, review, rejected.

status === "completed" significa que o pipeline rodou, não que a pessoa passou. Ramificar por ele para conceder acesso admite todo candidato rejeitado. Payloads de webhook não têm campo status nenhum — eles carregam type e verdict, que são ambos resultados. Ramifique por esses.

Testando localmente

Webhooks não conseguem alcançar localhost. Isto não é uma configuração que você possa relaxar: a URL precisa ser https://, e a proteção contra SSRF rejeita endereços de loopback e privados no momento do envio. Um túnel é a única forma de receber entregas reais em desenvolvimento.

FerramentaObservações
ngrokPlano gratuito; a escolha habitual
localtunnelGratuito, código aberto
Cloudflare TunnelGratuito; melhor para um ambiente de desenvolvimento de longa duração
ngrok http 3000
# Forwarding https://abc123.ngrok.io -> http://localhost:3000

# Coloque https://abc123.ngrok.io/webhooks/veridia em Settings → Webhook

Para exercitar o handler sem rodar uma verificação, reenvie um corpo com uma assinatura nova — veja o script de replay. Assine exatamente os bytes que você envia.

Checklist

  • Verifique Veridia-Signature antes de confiar no corpo. Sempre.
  • Rejeite se |agora - t| > 300 segundos.
  • Responda 2xx em até 10 segundos; persista primeiro, processe depois.
  • Deduplique por id (ou pelo cabeçalho Veridia-Event-Id). A entrega é ao menos uma vez.
  • Faça o switch em type. Não em event — não existe campo event.
  • Leia scores com chaves em snake_case, e faça verificação de null em scores.liveness.
  • Trate fieldsExtracted como dado pessoal, inclusive nos seus logs.
  • Registre id e verificationId em cada entrega — são eles que o suporte vai pedir.

O que vem a seguir