- Webhooks são reenviados por padrão em muitos sistemas quando há timeout/erro: duplicidade é “comportamento esperado” se você não se proteger.
- Idempotência não é só “tirar duplicado depois”; é desenhar o fluxo para ser seguro ao repetir.
- A chave idempotente precisa ser determinística (mesmo evento → mesma chave) e rápida de consultar.
- O ponto crítico é onde você tem efeito colateral: cobrança, pedido, nota, mensagem WhatsApp, criação de ticket.
1) Descubra qual é o evento (e de onde vem a duplicidade)
Antes de mexer no fluxo, confirme o motivo do replay: timeout no endpoint, política de retry do provedor, usuário clicando duas vezes, fila do gateway, ou webhook sendo disparado em “update” e “create”. Se você não sabe “qual evento é o mesmo evento”, não existe chave idempotente que resolva.
2) Defina a chave idempotente (idempotency key)
Use um identificador que venha do sistema de origem (ex.: event_id, message_id, payment_id, order_id + status). Se não existir, construa: normalize campos estáveis (tipo do evento + id do objeto + timestamp do evento arredondado) e gere um hash. Evite usar horário de chegada no webhook (isso muda).
3) Escolha onde guardar a chave (storage de deduplicação)
Você precisa de um lugar consultável e persistente. Opções comuns: banco SQL (tabela de eventos processados), Redis/cache com TTL (para janelas curtas), ou uma planilha/tabela simples quando o volume é baixo. O importante é ter consulta rápida e condição de unicidade (unique) quando possível.
4) Faça a checagem logo no começo do fluxo
A primeira coisa após receber o webhook deve ser: “já vi essa chave?”. Se sim, encerre com sucesso (200/OK) e, se o sistema esperar resposta, devolva o mesmo payload/resultado (quando aplicável). Se não, registre a chave e siga.
5) Trave contra corrida (race condition)
Dois webhooks iguais podem chegar quase ao mesmo tempo. Se você fizer só um “SELECT antes” sem travar, ambos passam. Prefira: inserir com chave única (e se der conflito, tratar como duplicado) ou usar lock/SETNX (no Redis). Isso é o que separa deduplicação “bonitinha” de deduplicação que aguenta produção.
6) Modele o comportamento: ignorar, reprocessar ou “retornar o mesmo resultado”
Nem todo duplicado deve ser descartado. Para cobrança/pedido/emissão de nota, o padrão é não repetir efeito colateral. Para um webhook de “consulta de status”, pode ser ok reprocessar. Em muitos casos, o melhor é responder como se tivesse processado, mas sem executar novamente.
7) Defina janela de retenção (TTL) e auditoria
Guarde chaves por tempo compatível com o negócio: para pagamentos e pedidos, geralmente você quer dias ou meses (depende de chargeback, reconciliação e disputa). Quando não der para guardar “para sempre”, registre também logs/auditoria do processamento para provar o que aconteceu.
O que acontece sem idempotência (o roteiro do desastre)
Você recebe um webhook de pedido pago. Seu fluxo manda mensagem para o cliente, cria nota no ERP e dispara separação no estoque.
O provedor reenviou o webhook porque seu endpoint demorou a responder (ou porque caiu por 2 segundos). Resultado: duas notas, duas separações, duas mensagens. A equipe “resolve no braço”, e o custo real fica invisível até estourar.
Idempotência é o cinto de segurança: você não planeja bater, mas o impacto diminui quando acontece.
Como escolher uma chave idempotente que não te traia
A melhor chave é a que já existe no evento: um event_id único gerado pelo sistema de origem. Se não existir, você precisa construir uma chave que represente o “mesmo acontecimento”.
Regra prática: a chave deve mudar quando o evento for realmente diferente, e ficar igual quando for replay do mesmo evento.
- Boas chaves (exemplos): payment_id; event_id; order_id + tipo_evento; message_id.
- Chaves perigosas: timestamp de chegada; número incremental do seu próprio fluxo; hash do payload inteiro (pequenas variações quebram).
- Quando há “update”: inclua versão/estado (ex.: order_id + status_atual) para não bloquear transições legítimas.
Padrões de implementação (com e sem banco)
Você não precisa de uma arquitetura gigantesca para começar, mas precisa de consistência. Abaixo, o que costuma funcionar na prática — do mais robusto ao mais simples.
| Abordagem | Como funciona | Quando usar | Risco/limite |
|---|---|---|---|
| Banco SQL com chave única | Insere (idempotency_key) em tabela com UNIQUE; conflito = duplicado | Produção, alto impacto (cobrança/nota/pedido) | Exige banco e cuidado com transações |
| Redis/Cache com SETNX + TTL | Cria lock/chave se não existir; se existir, bloqueia repetição | Alta taxa de eventos, janela curta de dedupe | Se TTL for curto demais, duplicidade volta |
| Planilha/DB no-code simples | Busca chave; se não existir, grava e continua | Baixo volume e baixo risco | Race condition e performance; precisa disciplina |
| Deduplicação pós-fato | Deixa duplicar e tenta “consertar” depois | Quase nunca é o ideal | Você já sofreu o efeito colateral |
Onde a maioria erra: registrar a chave tarde demais
O erro clássico é checar duplicidade depois de já ter criado o lead, emitido nota ou enviado WhatsApp. A checagem precisa ser no início, antes do efeito colateral.
Se seu fluxo faz múltiplas ações, pense em idempotência por etapa: às vezes você quer impedir duplicação de cobrança, mas permitir reenvio de mensagem (ou o inverso).
Checklist rápido para webhooks resilientes
Se você só aplicar idempotência, já melhora muito. Mas um webhook confiável costuma precisar de mais três coisas: timeout decente, retries com backoff e logs úteis.
- Responder rápido (200) quando possível; fazer processamento pesado em segundo plano se sua stack permitir.
- Tratar 429/rate limit com retry/backoff (sem “loop infinito”).
- Guardar: idempotency_key, timestamp, status (processado/erro), e um identificador do fluxo que processou.
- Alertar quando a taxa de duplicados ou erros subir (é sinal de instabilidade a montante ou a jusante).
Perguntas frequentes
Idempotência é a mesma coisa que deduplicação?
Qual status devo responder quando o webhook é duplicado?
Como lidar com dois webhooks idênticos chegando ao mesmo tempo?
E quando o evento é parecido, mas não é duplicado (ex.: pedido mudou de status)?
Leia também
Webhook no n8n: como receber, tratar e responder HTTP (sem gambiarra)
Aprenda a criar um webhook no n8n, validar assinatura, tratar payload, evitar duplicidade e responder HTTP 200/400/500 com padrão de produção.
Ler →
Rate limit, retry e backoff: como parar de quebrar automação em API (n8n, Make, Zapier)
Guia direto para lidar com rate limit (429), retries e backoff em automações com APIs. Aprenda padrões práticos para n8n, Make e Zapier e evite falhas intermitentes, duplicidades e banimentos.
Ler →
Erro 429 (Rate Limit) em automações: como resolver sem travar o fluxo
Entenda o que causa o erro 429 (rate limit), como calcular limites, aplicar retry com backoff, filas e idempotência para automações com API que não quebram em pico.
Ler →
n8n vs Make para workflows long-running: filas, retries e tarefas que não podem falhar
Entenda qual ferramenta lida melhor com automações longas (minutos/horas), filas, rate limit e reprocessamento: n8n ou Make. Comparação prática com cenários reais no Brasil.
Ler →