Pular para o conteúdo
Webhooks

Receba o resultado por webhook

Em vez de consultar a verificação em laço, cadastre um endpoint e receba um POST assinado assim que a decisão sai. Cada entrega leva a assinatura HMAC-SHA256 e um carimbo de tempo para você conferir a origem.

Documentação › Webhooks

1. Cadastre o endpoint

Cadastre pelo console, em Webhooks, ou pela API. O destino precisa ser público e responder em HTTPS; endereços internos são recusados na hora do cadastro.

curl -X POST https://api.provalidface.com.br/api/v1/webhooks \
  -H "Authorization: Bearer vf_test_sua_chave_de_teste" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://api.suaempresa.com.br/webhooks/validface", "description": "Produção"}'

A resposta 201 traz o secret de assinatura uma única vez. Guarde agora: ele não volta a ser exibido e fica cifrado no banco.

{
  "id": "9a0f...",
  "url": "https://api.suaempresa.com.br/webhooks/validface",
  "description": "Produção",
  "is_active": true,
  "created_at": "2026-09-30T12:00:00Z",
  "secret": "troque_pelo_segredo_que_veio_na_resposta",
  "aviso": "Guarde o segredo agora: ele nao volta a ser exibido."
}

2. Eventos entregues

A plataforma entrega três eventos ao seu endpoint. O corpo repete os campos de decisão (status, score, reasons) e acrescenta o campo event. O formato difere do GET da verificação: não traz session_url nem a lista de etapas.

EventoQuandoConteúdo
verification.completedA verificação foi decidida automaticamente pelo pipeline.Traz status (approved, rejected ou review), score e reasons.
verification.expiredA sessão passou do prazo sem o titular concluir.status expired; nenhuma etapa decidiu a sessão.
verification.reviewedUm analista da sua empresa fechou um caso na revisão manual.status final approved ou rejected, com MANUAL_REVIEW_* no fim de reasons.

Os demais registros verification.* (por exemplo verification.decided) ficam na trilha de auditoria interna e não são enviados por webhook.

3. Verifique a assinatura

Cada entrega chega com dois cabeçalhos:

  • X-ValidFace-Signature: o valor sha256=<hmac>.
  • X-ValidFace-Timestamp: o instante do envio, em segundos desde a época (Unix).

A assinatura cobre o carimbo de tempo mais o corpo bruto, nesta forma, o que impede reaproveitar um corpo válido com data antiga:

sha256 = HMAC_SHA256(secret, f"{timestamp}." + corpo_bruto)

Recalcule com o segredo do endpoint sobre o corpo exatamente como chegou (não reserialize o JSON) e compare em tempo constante:

import hashlib
import hmac

def assinatura_confere(secret, corpo_bruto, timestamp, cabecalho_assinatura):
    esperado = "sha256=" + hmac.new(
        secret.encode(),
        f"{timestamp}.".encode() + corpo_bruto,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(esperado, cabecalho_assinatura)

Exemplo conferível

Com o segredo whsec_exemplo_troque_pelo_segredo_do_seu_endpoint, o cabeçalho X-ValidFace-Timestamp: 1730419200 e o corpo abaixo:

{"cost": "0.52", "created_at": "2026-09-30T12:00:00+00:00", "decided_at": "2026-09-30T12:00:07+00:00", "engine_versions": {"cpf": "[email protected]", "document_classify": "[email protected]", "face_match": "[email protected]", "liveness": "[email protected]", "ocr": "[email protected]"}, "event": "verification.completed", "external_reference": "pedido-1042", "id": "3f1c8e0a-9d4b-4c2e-8a10-2b7c5e9f0a11", "journey": "padrao", "reasons": [{"code": "APPROVED", "message": "Verificacao aprovada."}], "score": 95, "status": "approved"}

A assinatura é:

X-ValidFace-Signature: sha256=7ee06d5f42a1f03ad3845fc86b2a8cf9481c051f3fa4161f68158b358fde841a

Este valor foi calculado pela mesma função que assina as entregas em produção.

4. Retentativas e idempotência

Responda 2xx para confirmar o recebimento. Sem isso, a entrega é reagendada com espera progressiva, até 5 tentativas, e então marcada como falha. Há uma entrega por combinação de verificação, endpoint e evento, então a reexecução do pipeline não vira dois POST do mesmo evento. Ainda assim, trate o recebimento de forma idempotente pelo id da verificação: registre o que já processou e ignore a repetição.

5. Reenvie uma entrega

Se o seu endpoint ficou fora do ar e a entrega esgotou as tentativas, reenvie pelo console ou pela API. O reenvio zera as tentativas, volta a entrega para a fila e responde 202.

curl -X POST https://api.provalidface.com.br/api/v1/webhooks/deliveries/ID_DA_ENTREGA/resend \
  -H "Authorization: Bearer vf_test_sua_chave_de_teste"

Uma entrega ainda em andamento responde 409 (nenhum reenvio disparado, para não gerar POST duplo); fila indisponível responde 503 e a entrega fica pendente para reenvio automático.

6. Teste local com um túnel

O destino precisa ser público, então em desenvolvimento use um túnel para expor a sua máquina: cloudflared tunnel --url http://localhost:8000 ou ngrok http 8000 geram uma URL HTTPS temporária. Cadastre essa URL como endpoint e dispare uma verificação de sandbox para ver o POST chegar.