Fundamentos
Webhook de KYC: como tratar aprovado, reprovado e pendente
A maioria dos bugs de integração de KYC está nos estados intermediários. Veja a máquina de estados completa, idempotência e verificação de assinatura.

Um webhook de KYC não entrega só dois estados. Na API da Shiel ID, a sessão de verificação pode assumir cinco status: created, started, approved, rejected e pending. Aprovado e reprovado são os fáceis. A maioria dos bugs de integração mora nos estados intermediários, que muita integração deixa sem tratamento.
Verificação de identidade é operação assíncrona por natureza: leitura de documento, comparação biométrica, consulta a base externa e, em parte dos casos, análise humana. Nada disso cabe no tempo de uma requisição HTTP. O erro que se repete em integração é tratar o retorno da chamada de criação como se fosse a decisão. Ele não é. Ele é um protocolo de recebimento.
A máquina de estados completa
A tabela abaixo usa os status que a API da Shiel ID envia pelo webhook, descritos na documentação da API da Shiel ID. Outros fornecedores usam nomes diferentes, e alguns acrescentam estados de expiração ou cancelamento: confirme a nomenclatura exata na documentação antes de escrever código.
- created. O que significa: A sessão de onboarding foi criada e já tem hash e link da jornada. É final?: Não. O que seu sistema deve fazer: Persistir a hash e encaminhar o usuário para o link da jornada.
- started. O que significa: A jornada de verificação foi iniciada. É final?: Não. O que seu sistema deve fazer: Registrar o andamento, sem liberar nada.
- pending. O que significa: A verificação ainda não tem decisão final. É final?: Não. O que seu sistema deve fazer: Exibir espera informada, sem liberar nada, e acompanhar até o status final.
- approved. O que significa: A verificação concluiu com resultado positivo; o payload traz dados do documento e da face. É final?: Sim. O que seu sistema deve fazer: Confirmar na consulta de status, liberar o acesso e registrar o resultado com carimbo de tempo.
- rejected. O que significa: A verificação concluiu com resultado negativo. É final?: Sim. O que seu sistema deve fazer: Bloquear, registrar o motivo e acionar a política de recurso.
Três observações economizam retrabalho. Pending não é erro nem reprovação: tratá-lo como falha joga fora usuário legítimo que ainda não recebeu decisão. Sessão que fica em created ou started e nunca chega a um status final também não é reprovação, porque um cadastro abandonado não afirma nada sobre a identidade da pessoa, e misturar os dois contamina a métrica de aprovação. E toda reprovação precisa ficar registrada do seu lado com o motivo estruturado, para separar falha de captura, divergência de dado e suspeita de fraude.
Por que o retorno síncrono nunca libera acesso
A chamada de criação devolve um identificador e um estado inicial. Se o seu código lê esse retorno e conclui que o usuário está verificado, você construiu uma porta que abre sozinha.
A decisão ainda não existe naquele momento: processamento biométrico, consulta externa e fila de exceção acontecem depois. O redirecionamento de volta ao seu site no fim da jornada, como o returnUrl na Shiel ID, também não serve para autorizar, porque passa pelo navegador do usuário, e o que passa pelo cliente é manipulável. Casos que caem em análise humana podem levar minutos ou horas, e uma integração que só entende o síncrono costuma resolver o impasse liberando acesso.
A regra prática: o estado que autoriza é sempre o que chegou pelo webhook e foi confirmado por uma consulta ao endpoint de status do fornecedor. Na API da Shiel ID, essa consulta é o GET /getHashData, que devolve status, score, currentStep e os carimbos de criação e de conclusão da hash.
Como implementar o consumidor de webhook em sete passos
1. Exponha um endpoint dedicado e idempotente. Ele recebe, valida, grava e responde. Nada de regra de negócio dentro do handler.
2. Autentique a origem antes de confiar no corpo. Quando o fornecedor assina o payload, o padrão descrito na especificação Standard Webhooks é HMAC com SHA-256, com identificador do evento, carimbo de tempo e assinatura no cabeçalho. Recalcule sobre o corpo bruto, compare em tempo constante e rejeite o que não bater, inclusive evento com carimbo fora de uma janela curta. Se o fornecedor não assina, trate o webhook apenas como aviso e decida pela consulta de status do passo 6.
3. Responda 2xx rápido e processe depois. Confirme o recebimento e empilhe o evento. Processamento pesado dentro do handler gera timeout e, se o fornecedor faz retentativa, você passa a receber o mesmo evento várias vezes.
4. Deduplique cada evento. Use o identificador do evento quando o fornecedor enviar um. Na falta dele, a combinação do identificador da verificação, como a hash na Shiel ID, com o status recebido serve de chave. Guarde a chave com restrição de unicidade. Se já existe, descarte e responda 2xx. Reentrega é comportamento esperado, não anomalia.
5. Descarte evento fora de ordem. Guarde a versão ou o carimbo de tempo do último evento aplicado a cada verificação e ignore qualquer evento anterior. Sem isso, uma reentrega de "pending" reverte um "approved" já registrado.
6. Confirme na fonte antes de agir. Ao receber um estado final, consulte o endpoint de status com o identificador da verificação e aja sobre a resposta dessa consulta. O webhook é gatilho, a consulta é a verdade.
7. Registre transição, não só estado atual. Grave cada mudança com origem, carimbo de tempo, motivo e payload. Esse histórico sustenta auditoria, suporte ao usuário recusado e investigação de fraude.
O passo a passo de integração está em como integrar uma API de KYC, e a referência completa está na documentação da API da Shiel ID.
Idempotência, reentrega e ordem: os três que quebram em produção
- Evento duplicado. Como se manifesta: O mesmo resultado processado duas vezes, notificação enviada em dobro. Tratamento: Chave de unicidade pelo identificador do evento, com descarte silencioso.
- Reentrega após timeout. Como se manifesta: Pico de eventos repetidos depois de uma instabilidade sua. Tratamento: Responder 2xx antes de processar e enfileirar o trabalho.
- Ordem fora de sequência. Como se manifesta: Estado final sendo sobrescrito por estado intermediário antigo. Tratamento: Comparação de versão ou carimbo de tempo antes de aplicar a transição.
- Evento perdido. Como se manifesta: Verificação parada em status não final para sempre. Tratamento: Rotina de reconciliação periódica que consulta o status de tudo que não é final.
- Origem não verificada. Como se manifesta: Qualquer um posta "approved" no seu endpoint. Tratamento: Verificação de assinatura quando o fornecedor assina e, sempre, confirmação na consulta de status antes de agir.
A reconciliação é um item que muita equipe deixa para depois. Entrega de webhook não é certa, e uma varredura periódica dos registros não finais fecha o buraco sem depender de entrega perfeita.
O estado que ninguém modela: a reverificação
A máquina de estados descrita aqui vale para uma verificação. A conta, porém, é reverificada ao longo da vida: troca de dispositivo, mudança de dado cadastral, operação de valor atípico, alteração societária no caso de empresa. Na API da Shiel ID, por exemplo, a validação facial em transação cria uma nova hash, associada ao último KYC aprovado do usuário.
Se o seu modelo de dados trata verificação como atributo booleano do usuário, cada reverificação sobrescreve a anterior e o histórico se perde. Modele verificação como entidade própria, com muitas por usuário, cada uma com sua máquina de estados, seu motivo de disparo e seu resultado preservado. É uma decisão de schema barata no início e cara de corrigir depois.
O que fazer agora
- Liste os estados que o seu código trata hoje e compare com os cinco da tabela. Todo estado não tratado vira caso preso em produção.
- Verifique se o seu handler valida assinatura sobre o corpo bruto e rejeita evento com carimbo de tempo antigo.
- Adicione restrição de unicidade pelo identificador do evento e teste reenviando o mesmo payload três vezes seguidas.
- Implante a rotina de reconciliação para registros que passam de um limite de tempo sem estado final.
- Confirme com o fornecedor os nomes exatos de evento e de estado, a política de retentativa e o prazo de retenção do histórico.
Perguntas frequentes
Posso liberar o acesso com base na resposta da chamada de criação? Não. Esse retorno é um protocolo de recebimento, não uma decisão. O estado que autoriza é o que chega pelo webhook e é confirmado por consulta ao endpoint de status.
O que fazer quando o mesmo evento chega duas vezes? Descartar e responder 2xx. Reentrega é comportamento esperado de qualquer fornecedor. A proteção é uma chave de unicidade pelo identificador do evento, gravada antes do processamento.
Como evitar que um evento antigo sobrescreva um resultado final? Guardando a versão ou o carimbo de tempo da última transição aplicada e ignorando qualquer evento anterior a ela. Sem essa comparação, uma reentrega reverte um estado final.
Pending deve contar como reprovação? Não. É um status intermediário, sem decisão final. Contá-lo como reprovação distorce a métrica e descarta usuário legítimo que ainda pode ser aprovado.
E se o webhook não chegar? Para isso existe a reconciliação: uma rotina periódica consulta o status de todas as verificações que passaram do tempo esperado sem estado final e atualiza o registro.
Para avaliar como isso se aplica à sua operação, fale com o time comercial da Shiel ID.