Configuração de webhooks

Guia passo a passo para registrar um endpoint de webhook da Certivant, guardar o segredo de assinatura, enviar um evento de teste, rotacionar segredos e configurar substituições por formulário.

Os webhooks permitem que a Certivant envie as mudanças de estado para o seu backend em tempo real. Em vez de consultar GET /requests/:id até que o processamento termine, a Certivant faz um POST para uma URL sob o seu controle sempre que ocorre um evento de interesse.

Esta página cobre a configuração única. Para a verificação de assinatura que você precisa implementar antes de ir para produção, veja Verificação de assinatura.

O que você vai precisar

  • Uma URL HTTPS acessível publicamente, que aceite requisições POST com corpo JSON. Localhost serve durante o desenvolvimento, se você usar um túnel (ngrok, Cloudflare Tunnel, tailscale funnel).
  • A função Owner ou Admin na organização da Certivant — membros não podem alterar as configurações de webhook.
  • Uma forma de armazenar segredos — você receberá um segredo de assinatura HMAC que deve manter junto com as suas outras credenciais de produção.

1 — Crie o seu endpoint

Um manipulador mínimo tem esta aparência (mostrado com Node + Hono; qualquer framework funciona):

app.post("/webhooks/certivant", async (c) => {
  const body = await c.req.text();   // Read RAW body — needed for signature check
  const signature = c.req.header("X-Certivant-Signature-V2");
  const payload = JSON.parse(body);

  // Connectivity ping (the dashboard "Test" button) — ack immediately.
  // It carries `test: true` and mutates nothing, so no signature is required.
  if (payload.test === true || payload.event === "kyc.webhook.test") {
    return c.json({ ok: true }, 200);
  }

  // Verify the signature BEFORE trusting a real event, and reject if it fails.
  // See /developer/webhooks/signature-verification for the verify helper
  // (checks `t=<unix>,v1=<hex>` over `${t}.${body}`, 300 s tolerance).
  if (!verifyCertivantSignature(body, signature, process.env.CERTIVANT_WEBHOOK_SECRET!)) {
    return c.json({ error: "invalid signature" }, 401);
  }

  // Acknowledge fast — Certivant will retry if you take > 10s
  c.executionCtx.waitUntil(handlePayload(payload));
  return c.json({ received: true }, 200);
});

As duas regras inegociáveis:

  1. Responda com 2xx em até 10 segundos. A Certivant trata qualquer outra coisa (5xx, 4xx, timeout, erro de conexão) como falha de entrega e tenta novamente com espera exponencial.
  2. Leia o corpo bruto antes de interpretar o JSON. A verificação de assinatura calcula o hash da sequência exata de bytes que a Certivant enviou. Se o seu framework interpretar o JSON primeiro, você gerará uma string diferente, e a assinatura não vai bater.

2 — Configure a URL no painel

Entre no painel da Certivant com uma conta de Owner ou Admin.
Acesse KYC → Configurações → Webhooks.
Cole a URL do seu endpoint em Webhook URL, por exemplo https://api.yourapp.com/webhooks/certivant. Somente HTTPS — HTTP simples é recusado.
Clique em Salvar. A Certivant gera um segredo de assinatura no primeiro salvamento (visível apenas uma vez — copie-o para o seu ambiente agora).
O segredo tem o prefixo whsec_. Guarde-o no seu gerenciador de segredos como CERTIVANT_WEBHOOK_SECRET.

O segredo é exibido apenas uma vez, quando você o gera ou rotaciona — não há um botão de "mostrar o segredo novamente", então guarde-o imediatamente. Se você o perder, rotacione-o (veja abaixo) para obter um novo.

3 — Envie um evento de teste

Dois botões ajudam a verificar a integração:

  • Test (ao lado da URL do webhook) envia um ping leve de conectividade kyc.webhook.test com test: true. Ele confirma apenas que o seu endpoint está acessível e retorna 2xx — não precisa de dados de requisição e funciona mesmo antes de você armazenar um segredo de assinatura. Confirme o recebimento imediatamente (veja o manipulador acima).

  • Enviar evento de teste (KYC → Desenvolvedor → Eventos de webhook) permite escolher o evento — o ping kyc.webhook.test ou um payload de exemplo de kyc.request.created / approved / rejected / escalated — e faz o POST assinado exatamente como uma entrega real (X-Certivant-Signature-V2 mais os cabeçalhos X-KYC-*, timestamp Unix e o segredo do seu webhook). Os payloads de exemplo trazem "test": true e um requestId falso. Use-o para:

  • Confirmar que a URL está acessível a partir da infraestrutura da Certivant.

  • Confirmar que a sua verificação de assinatura aceita uma assinatura válida.

  • Ver o formato exato do payload antes de qualquer evento real ser disparado.

Você também pode disparar um teste programaticamente — útil quando quiser automatizar os testes da integração:

curl -X POST https://app.certivant.com/api/kyc/settings/webhook-test \
  -H "Cookie: better-auth.session_token=..." \
  -H "Content-Type: application/json" \
  -d '{"event":"kyc.request.approved"}'
# returns { status: 200, latencyMs: 142, event, signed: true, headers: [...], responsePreview } if your endpoint replied

O corpo é opcional (padrão kyc.webhook.test). O endpoint recebe um cookie de sessão, não uma chave de API — é uma ação do lado do painel.

4 — Rotacione o segredo de assinatura

Quando um segredo for exposto (ou anualmente, como boa prática):

Abra KYC → Configurações → Webhooks.
Clique em Rotacionar segredo. A Certivant gera um novo valor whsec_… e o exibe uma vez.
Atualize a sua variável de ambiente com o novo valor.
Faça o deploy. Depois do deploy, o segredo antigo deixa de valer — quaisquer novas tentativas em andamento que ainda carreguem a assinatura antiga falharão na verificação do seu lado, e a Certivant as tratará como não entregues.

Não há janela de sobreposição do lado do remetente: as novas entregas são assinadas somente com o novo segredo. As receitas de verificação em Verificação de assinatura aceitam uma lista de segredos e vários valores v1=, então você pode implantar um verificador que confie tanto no segredo antigo quanto no novo antes de rotacionar. Caso contrário, rotacione em um período de pouco movimento e acompanhe os seus logs em busca de falhas de verificação logo depois.

Substituição de webhook no nível do formulário

Requisições de KYC criadas por meio de um formulário podem substituir a URL padrão de webhook da organização por uma URL própria do formulário. Configure isso na página de configurações do formulário, no painel. É útil quando você tem várias integrações compartilhando a mesma conta da Certivant — aponte cada formulário para a equipe que o possui.

O segredo de assinatura é compartilhado entre a URL padrão e as substituições por formulário. O cabeçalho de assinatura é idêntico, independentemente da URL que recebe o evento.

O que não gera webhook

Algumas ações não disparam webhooks:

  • Criação, revogação ou alteração de escopos de chaves de API.
  • Alterações de configurações (edições de white-label, ajustes de limiares de KYC).
  • Downloads de documentos.
  • Comentários de revisão manual.

Se você precisar reagir a essas ações, consulte o Registro de atividades do painel — ainda não existe uma API pública para ele.

Próximos passos

  • Verificação de assinatura — faça isso antes de ir para produção. Sem ela, qualquer pessoa pode forjar requisições para a sua URL de webhook.
  • Eventos — o catálogo completo de tipos de evento e payloads.
  • Novas tentativas — o que acontece quando o seu endpoint está fora do ar.