Article
Idempotência para webhooks: guia pronto para produção
Evite efeitos duplicados causados por retries com padrões práticos de idempotência em webhooks: deduplicação, ordenação, replay, assinaturas e testes.
Article details
Published
August 19, 2026
Reading time
7 min
Main sections
9
Se um webhook pode fazer retry, assuma que você vai ver o mesmo evento mais de uma vez. Isso não é um caso de borda. É o modelo de entrega normal da maioria dos provedores, e a única resposta segura é tornar seus efeitos colaterais idempotentes.
O objetivo não é entrega literalmente exactly-once. O objetivo é efeito exactly-once: uma atualização de cliente, uma fatura, uma transição de estado — mesmo que o provedor envie o payload duas vezes, três vezes ou fora de ordem.
O checklist de modo live da Stripe deixa essa expectativa explícita: handlers de webhook em produção devem tolerar notificações duplicadas e entrega fora de ordem. Em outras palavras, comportamento seguro contra duplicatas não é um diferencial. Faz parte do contrato de integração.
Por que duplicatas acontecem
Duplicatas de webhook costumam vir de um comportamento normal de entrega:
- timeout no lado do remetente
- falha de rede transitória
- crash do consumidor após processamento parcial
- retry após um acknowledgement ambíguo
É por isso que sistemas de webhook se comportam mais como entrega at-least-once do que exactly-once. Se o consumidor não tolera essa realidade, efeitos colaterais duplicados tornam-se inevitáveis.
O que idempotência em webhooks realmente significa
Idempotência não é só "ignorar duplicatas". É um contrato sobre efeitos colaterais duráveis. O mesmo evento de entrada deveria levar ao mesmo resultado de negócio, independentemente de quantas vezes for entregue.
Isso significa que o consumidor precisa decidir qual é a unidade de trabalho:
- um ID de evento do provedor
- uma chave de negócio como
subscription_id + billing_period - uma chave de idempotência gerada e atrelada à ação real
O padrão idempotent consumer é um bom modelo mental aqui: a entrega de mensagens pode se repetir, então o efeito durável também precisa ser seguro para repetir.
Padrões essenciais: escolha um e depois endureça
Fluxos diferentes precisam de defesas diferentes. Use esta comparação como a lente principal de design:
| Padrão | Melhor para | Força | Trade-off |
|---|---|---|---|
| Store de deduplicação por ID de evento | Provedores que enviam identificadores estáveis de evento | Simples e eficaz para entregas repetidas | Depende de identidade confiável da fonte |
| Unique constraint em chave de negócio | Transações de negócio com chave única natural | Protege a escrita final na camada do banco | Exige modelagem de domínio cuidadosa |
| Chaves de idempotência | Ações repetidas disparadas por cliente ou workflow | Funciona bem quando retries podem vir de múltiplos atores | Geração de chave e TTL precisam ser explícitos |
| Inbox/outbox | Fluxos com múltiplos efeitos colaterais e necessidade de replay | Melhora auditabilidade e recuperação | Mais peças móveis e mais armazenamento |
O default entediante mas forte costuma ser duas camadas: curto-circuitar duplicatas cedo com um registro de deduplicação e depois proteger a escrita final com uma unique constraint.
Como implementar o consumidor com segurança
Comece identificando a primeira escrita durável. É ali que a duplicata precisa ser bloqueada.
O fluxo seguro é:
- verificar autenticidade
- extrair o ID do evento ou a chave de negócio
- checar o store de deduplicação ou a unique constraint
- se já foi processado, retornar sucesso
- persistir o marcador do evento e executar o efeito colateral na mesma fronteira transacional, quando possível
- registrar metadados suficientes para replay e debugging
O design mais perigoso é marcar o evento como processado depois do efeito colateral. Se o processo cair nessa janela, um retry pode executar o efeito de novo.
Chaves de idempotência, TTLs e ordenação
Se o provedor não envia um identificador estável de evento, você pode precisar construir sua própria chave de idempotência a partir da ação de negócio. A chave deve representar o efeito que você quer proteger, não apenas o payload bruto.
A política de TTL também importa:
- curto demais, e retries legítimos ficam fora da janela de proteção
- longo demais, e você mantém estado desnecessário para sempre
Ordenação é a outra armadilha oculta. Muitos provedores não garantem ordem global estrita entre eventos relacionados. Se o seu consumidor assume que created sempre chega antes de updated, você vai eventualmente bater num estado impossível.
Segurança e autenticidade
Verificação de assinatura é separada da idempotência, mas precisa acontecer primeiro. Um evento assinado e duplicado continua sendo tratado como duplicata. Um evento sem assinatura ou adulterado deve ser rejeitado antes de chegar à lógica de negócio.
Se o provedor suporta proteção contra replay ou validação de timestamp, use. Autenticidade e segurança de replay fazem parte da mesma fronteira.
O guia da Stripe sobre verificação de assinatura e proteção contra replay é um bom exemplo concreto dessa fronteira: verifique o remetente, preserve o raw body e rejeite entregas velhas ou adulteradas antes de a lógica de negócio rodar.
Replay e reprocessamento
Replay não deveria ser um improviso inventado durante um incidente. Se você espera reprocessar payloads, guarde evidência suficiente para fazer isso com segurança:
- ID do evento
- sistema de origem
- timestamp de recebimento
- status de processamento
- chave de negócio usada na deduplicação
- correlation ID para tracing
Isso transforma replay em uma capacidade operacional, em vez de uma aposta.
Checklist de produção + casos de teste
Use este checklist antes de subir para produção:
- confirme a semântica de retry do provedor
- identifique a chave estável de deduplicação
- crie o store de deduplicação antes do primeiro efeito colateral
- torne a verificação de assinatura obrigatória
- defina o TTL de deduplicação e a política de replay
- logue ID do evento, status de processamento e correlation ID
- torne os efeitos colaterais seguros para replay
- defina como eventos fora de ordem convergem
- decida quando replay manual ou tratamento de DLQ é necessário
Rode também estes testes:
- mesmo evento chega duas vezes em rápida sucessão
- mesmo evento chega após um crash entre a escrita de deduplicação e o efeito colateral
- dois eventos mapeiam para o mesmo objeto de negócio
- update fora de ordem chega antes do create
- replay válido chega depois que o original já foi tratado
- payload sem assinatura é rejeitado antes de qualquer escrita
Modos de falha comuns
O erro mais comum é tratar deduplicação como uma consulta de cache em vez de uma garantia de durabilidade. Se o registro de deduplicação não é escrito de forma atômica junto com o estado de negócio, duplicatas vão vazar.
Outro erro é assumir que o provedor vai reenviar apenas um payload idêntico para sempre. Na prática, o efeito de negócio pode importar mais do que o payload bruto.
O terceiro erro é a observabilidade ruim. Se você não consegue rastrear um webhook da ingestão ao efeito colateral, não vai saber se a duplicata é real ou se o seu próprio sistema fez retry internamente. É por isso que este tema conecta diretamente com Observabilidade para engenheiros de produto, Sistemas event-driven sem folclore e Filas não são bala de prata.
Related articles
Article
Sistemas event-driven sem folclore
Leia também: Sistemas event-driven sem folclore, Filas não são bala de prata e Arquitetura serverless AWS para integrações.
Article
Filas não são bala de prata
Leia também: Sistemas event-driven sem folclore, Filas não são bala de prata e Arquitetura serverless AWS para integrações.
Article
Arquitetura serverless AWS para integrações
Leia também: Sistemas event-driven sem folclore, Filas não são bala de prata e Arquitetura serverless AWS para integrações.
Need help applying this?
Turn the trade-off into a practical product decision.
Se você precisa de integrações resilientes que sobrevivam a retries, replay e ao barulho real de produção, o primeiro passo é descobrir onde o seu handler atual vaza duplicatas. Comece com um get in touch.
FAQ
Common questions before committing to the pattern.
Qual é a diferença entre idempotência e deduplicação?+
Deduplicação é um mecanismo. Idempotência é o comportamento que você quer: a entrega repetida ainda produz o mesmo resultado de negócio.
Devo usar o payload do webhook como chave de deduplicação?+
Normalmente não. Prefira um ID de evento estável do provedor ou uma chave de negócio atrelada ao efeito colateral real.
Preciso de store de deduplicação e unique constraint ao mesmo tempo?+
Nem sempre, mas é um default forte. O store de deduplicação curto-circuita o trabalho repetido, e a unique constraint protege a escrita durável final.
Quanto tempo os registros de deduplicação devem viver?+
O bastante para cobrir a janela de retry do provedor e as suas necessidades de replay. O TTL certo depende do impacto no negócio e da política operacional.
E se o provedor não enviar um ID de evento estável?+
Modele a sua própria chave de idempotência a partir da ação de negócio. Em muitos sistemas, isso significa um ID de pedido, um ID de assinatura ou um período de cobrança, em vez do payload bruto.