Reconciliação de webhook, o plano para quando o evento não chega

UNIFOKAL6 min de leituraProduto e integração

Webhook duplica, atrasa e some. Deduplicação por identificador, decisão pelo estado e janela de reconciliação ativa formam o desenho de integração que sobrevive aos três modos de falha.

Toda integração por webhook carrega um contrato honesto e desconfortável: a plataforma promete entregar o evento ao menos uma vez, e nada além disso. A promessa não inclui entregar exatamente uma vez, entregar em ordem, nem entregar dentro de um prazo. Quem integra uma API de verificação de identidade e trata o webhook como um fluxo confiável e ordenado de notificações está construindo sobre uma garantia que ninguém deu.

A consequência aparece nos piores momentos. O cliente conclui a verificação, o resultado existe na plataforma, e a conta continua travada porque o evento se perdeu numa janela de instabilidade do endpoint. Ou o oposto: o mesmo evento processado duas vezes credita duas vezes. Este artigo descreve o desenho de integração que aguenta os três modos de falha reais de um webhook: ele duplica, ele atrasa e ele some.

Os três modos de falha

A duplicação é consequência direta da entrega ao menos uma vez. O seu servidor recebe, grava no banco e demora para responder; o timeout da plataforma dispara antes da resposta, e a entrega é marcada como falha e reenviada. Do ponto de vista da plataforma, reenviar é a única atitude correta: ela não tem como saber se você processou. A RFC 9110 do IETF, que define a semântica do HTTP, formaliza o motivo: POST não é um método idempotente, então o remetente não pode assumir que repetir a requisição é inofensivo. Tornar a repetição inofensiva é trabalho do receptor.

O atraso vem das retentativas com intervalos crescentes. Se o seu endpoint ficou dez minutos fora do ar, os eventos daquele intervalo chegam depois, fora de ordem, misturados com eventos novos. Um evento antigo chegando depois de um novo não é anomalia, é o comportamento esperado de qualquer sistema de retentativa.

E o sumiço é o caso limite do atraso: toda política de retentativa tem um fim. Se o seu endpoint ficar indisponível por mais tempo que a janela total de reentrega, ou se ele responder sucesso e morrer antes de persistir, o evento se foi e nenhuma retentativa vai trazê-lo de volta. Integração que não tem plano para esse caso não tem plano.

Deduplicação pelo identificador do evento

Contra a duplicação, o mecanismo é conhecido: todo evento carrega um identificador único, e o receptor registra esse identificador no mesmo ato transacional que aplica o efeito. Chegou identificador repetido, ignora e responde sucesso. O detalhe que separa a implementação correta da ingênua é o mesmo ato transacional: verificar se o evento já existe e aplicar o efeito em transações separadas abre a corrida clássica em que duas entregas simultâneas passam juntas pela verificação. Uma restrição de unicidade no banco fecha a janela de vez, e o raciocínio completo está no artigo sobre idempotência em APIs.

Vale nomear o que a deduplicação não resolve: ela protege contra o mesmo evento duas vezes, não contra eventos diferentes que descrevem o mesmo fato. Uma verificação reprocessada pode gerar um evento novo com identificador novo. Por isso a deduplicação é a primeira camada, não a única.

Decida pelo estado, não pela chegada

Contra a desordem, a regra é decidir pelo estado que o evento descreve, nunca pela ordem de chegada. Estados de uma verificação têm progressão natural: pendente, em análise, aprovada ou recusada. Se o seu registro local já está num estado final e chega um evento descrevendo um estado anterior, o evento é informação velha e deve ser ignorado. A comparação usa o carimbo de tempo do evento, gerado pela plataforma no momento do fato, não o horário de chegada ao seu servidor.

Esse princípio simplifica o código de forma surpreendente: em vez de uma máquina de estados que trata toda permutação de chegada, uma função que responde uma pergunta só, este evento é mais novo que o meu estado atual? Se não é, descarta. Se é, aplica.

A janela de reconciliação ativa

Contra o sumiço, só existe uma resposta estrutural: o webhook é notificação, não fonte de verdade. A fonte de verdade é o estado na plataforma, consultável pela API. A integração completa roda uma rotina periódica de reconciliação que lista as verificações locais ainda pendentes acima de uma certa idade e consulta o estado canônico de cada uma. Encontrou divergência, aplica a correção pelo mesmo caminho idempotente do webhook e registra a ocorrência numa métrica, porque reconciliação corrigindo com frequência é sinal de problema na entrega que merece investigação, não rotina silenciosa.

A mesma rotina é o plano de retorno de incidentes. Se o seu endpoint ficou horas fora, você não precisa reconstruir o que se perdeu na base de tentativa e erro: roda a reconciliação sobre o período afetado e o estado converge. Sem essa rotina, o retorno de incidente vira planilha manual e cliente reclamando de conta travada, o oposto do que uma integração assíncrona bem desenhada promete.

Dinheiro só se move pelo caminho confirmado

Uma decisão de arquitetura fecha o desenho: qualquer efeito financeiro ou irreversível acontece num único ponto do código, alimentado exclusivamente pelo caminho que valida, deduplica e ordena os eventos. Resposta síncrona de criação de sessão não credita, não libera e não recusa; ela só confirma que o pedido foi aceito. É a mesma lógica da integração segura de webhooks, que valida a assinatura HMAC, definida na RFC 2104 do IETF, antes de qualquer processamento: cada camada tem uma responsabilidade, e a camada que movimenta dinheiro é uma só.

Na UNIFOKAL, por exemplo, a reentrega de webhook insiste por até 24 horas com intervalos crescentes. Esse número existe como resiliência contra indisponibilidade prolongada do receptor, não como promessa de latência, e não muda a recomendação deste artigo: mesmo confiando na reentrega, o integrador mantém a rotina de reconciliação consultando o estado canônico, porque a janela de retentativa de qualquer fornecedor é finita e o seu fluxo de negócio não pode depender de ela nunca se esgotar.

O teste de aceitação da integração é direto e vale a pena automatizar no sandbox: entregue o mesmo evento duas vezes e confira efeito único; entregue dois eventos fora de ordem e confira o estado final correto; segure um evento além da janela e confira que a reconciliação o encontra. Integração que passa nesses três cenários está pronta para o comportamento real de produção, que é exatamente esse.

Fontes citadas

  • RFC 9110, do IETF, HTTP Semantics, sobre a semântica dos métodos e a definição de idempotência: rfc-editor.org
  • RFC 2104, do IETF, HMAC: Keyed-Hashing for Message Authentication: rfc-editor.org