Por que retries de webhooks geram mensagens duplicadas
Entenda como reenvios, filas e retries automáticos podem duplicar mensagens e como identificar a causa pelos IDs e metadados.

Por que as tentativas de reenvio de webhooks estão causando mensagens duplicadas?
As tentativas de reenvio de webhooks são úteis — até deixarem de ser. Um único timeout perdido pode virar duas mensagens, depois três, e de repente a mesma atualização de pedido, redefinição de senha ou aviso de envio aparece mais de uma vez. É aí que a pergunta “por que as tentativas de reenvio de webhooks estão causando mensagens duplicadas” deixa de ser teórica e começa a consumir tempo, especialmente quando a equipe tenta entender por que webhook duplica mensagem.
O primeiro erro é assumir que toda mensagem repetida significa que o remetente está com problema. Às vezes, o remetente reenviou exatamente como deveria. Às vezes, o seu receptor respondeu devagar demais. Às vezes, uma fila, um worker ou uma repetição de log gerou a cópia extra depois que o webhook já tinha chegado uma vez. São problemas diferentes, e pedem correções diferentes. Esse é o tipo de cenário em que faz sentido investigar reenvio de webhook mensagem duplicada.
Essas duplicatas estão vindo de reenvios ou de eventos de webhook separados?
Comece pelo teste mais simples: compare os payloads. Se duas entregas compartilham o mesmo ID de evento, o mesmo ID de entrega ou os mesmos metadados de assinatura, você provavelmente está diante do mesmo evento entregue mais de uma vez. Se os dados de negócio são parecidos, mas os IDs são diferentes, o remetente pode ter criado dois eventos reais, como duas ações de checkout ou duas mudanças de status.
Não confie apenas no timestamp. Duas entregas com 12 segundos de diferença ainda podem ser o mesmo evento, enquanto duas mensagens chegando no mesmo segundo ainda podem ser eventos separados. Um ciclo de retry e um verdadeiro evento duplicado podem parecer quase idênticos na caixa de entrada, por isso os IDs importam mais do que o olho humano. Saber como identificar retry de webhook exige olhar além do horário de chegada.
Há uma pista prática aqui. Se a sua equipe de suporte vê alertas de e-mail duplicados, mas os logs do webhook mostram um evento original e um retry com os mesmos metadados de entrega, o problema provavelmente está no transporte. Se os logs mostram dois payloads distintos, com IDs de evento diferentes, a duplicação vem de antes. Essa distinção economiza horas.
Qual comportamento de retry o remetente está realmente usando?
Provedores diferentes lidam com retries de formas diferentes. Alguns tentam automaticamente após um timeout. Alguns permitem que um operador clique em reenviar. Alguns fazem nova entrega após um código de falha temporária. Alguns fazem os três. Se você não souber qual está ativo, sua investigação vai andar em círculos.
Um reenvio manual costuma deixar um rastro humano: uma ação no painel, uma anotação de ticket ou um chamado de suporte. Retries automáticos tendem a seguir um padrão, muitas vezes com intervalos crescentes entre as tentativas. Políticas de nova entrega no lado do provedor podem ser mais discretas, porque o remetente pode tratar uma falha de rede, um problema de DNS ou uma resposta 5xx como motivo para tentar novamente sem ninguém tocar no painel.
Peça a política de retries do remetente por escrito. Um provedor pode tentar por 24 horas; outro pode parar após 3 tentativas; outro pode tratar qualquer resposta fora de 2xx como falha. Isso importa porque uma mensagem que “duplica” depois de 15 minutos pode ser apenas a tentativa 4 da mesma entrega.
O que faz uma entrega de webhook parecer várias mensagens a jusante?
O webhook pode estar certo. A duplicação pode acontecer depois. Uma única entrega pode desaguar em uma fila, depois em dois consumidores, depois em uma tarefa com retry e, por fim, em duas notificações de saída. O webhook original era uma mensagem; o seu sistema fez parecer que eram várias.
Isso é especialmente comum quando o handler grava em uma fila antes de confirmar um registro no banco, ou quando dois workers consomem a mesma tarefa após um visibility timeout. Um webhook vira várias ações a jusante porque o estado interno não estava travado. O resultado parece que o remetente reenviou, mas a duplicação real aconteceu dentro da sua própria stack.
Veja onde ocorre o primeiro efeito colateral. O e-mail foi enviado antes do insert no banco? A fatura foi marcada como paga depois que a tarefa de notificação começou? A tarefa foi reenfileirada porque o worker caiu no momento errado? Esses detalhes importam mais do que a mensagem de erro principal.
Como os IDs do payload e os metadados de entrega podem revelar a causa real?
Os IDs do payload são a melhor evidência. ID do evento, ID de entrega, contagem de tentativas, assinatura da requisição e timestamp do provedor podem mostrar se um webhook foi repetido ou se vários eventos separados foram emitidos. Se o remetente incluir um contador de tentativas, um número de entrega ou um hash da requisição, salve isso nos logs e no banco de dados.
Se precisar, monte uma pequena tabela de comparação. Três colunas bastam para um primeiro passo: o identificador do payload, os metadados de entrega e a ação de negócio executada. Quando o mesmo ID de evento aparece duas vezes, mas o seu sistema só agiu uma vez, o retry pode ser inofensivo. Quando o mesmo ID aparece duas vezes e o seu sistema agiu duas vezes, há uma lacuna de deduplicação.
| Campo | O que ele indica | Pista comum |
|---|---|---|
| ID do evento | Se o evento do webhook é o mesmo | Mesmo ID, entrega repetida |
| ID de entrega | Qual tentativa de transporte chegou até você | IDs de entrega diferentes, mesmo evento |
| Contador de tentativas | Quantas tentativas o remetente fez | Número maior após timeout |
| Assinatura | Se o corpo mudou no caminho | Incompatibilidade sugere payload alterado |
Sinais de uma duplicata real costumam ser entediantes — o que é bom. Mesmo payload, mesmos IDs, mesma ação, entrega repetida. Sinais de eventos separados são mais bagunçados: timestamps próximos, conteúdo parecido, mas IDs diferentes ou registros de origem diferentes. Essa é a diferença entre um problema de retry e um problema de produto.
Por que acknowledgements lentos frequentemente disparam entregas duplicadas?
A maioria dos remetentes de webhook espera uma resposta rápida 2xx. Se o seu servidor hesita por 8 segundos, atinge timeout em 10 ou retorna um reset de conexão, o remetente pode não saber se a mensagem foi processada. Então ele tenta de novo. Do ponto de vista dele, reenviar é mais seguro do que perder o evento.
É por isso que uma entrega pode ser bem-sucedida e ainda assim ser reenviada. A rede pode falhar depois que o seu app já gravou o registro, ou a resposta pode atrasar o suficiente para o remetente presumir falha. Uma única resposta tardia basta. O remetente não tem visibilidade perfeita do seu estado interno.
Uma consulta lenta ao banco é suficiente para iniciar a cadeia. O mesmo vale para um container “frio”, uma thread bloqueada ou uma fila que espera antes de devolver sucesso. Se o endpoint do webhook fizer qualquer coisa pesada antes de confirmar o recebimento, mensagens duplicadas ficam mais prováveis. Isso é um problema de transporte, não de conteúdo.
Se você quiser um padrão relacionado, o artigo sobre webhooks de e-mail para e-mails transacionais cobre a mesma ideia no tratamento de eventos de e-mail, onde timing e confirmação moldam o que será entregue em seguida.
E se o seu receptor processou o evento, mas ainda assim retornou falha?
Esse é complicado. O webhook pode criar o pedido, enviar a notificação ou atualizar o cadastro do cliente e, depois, lançar uma exceção ao montar a resposta. O remetente vê falha. Ele tenta novamente. Seu sistema agora tem dois efeitos colaterais de um único evento, e os logs podem discordar sobre qual etapa falhou primeiro.
Por isso, falhas parciais exigem atenção especial. Um handler que salva a mensagem e depois quebra antes de retornar 2xx pode já ter feito o trabalho real. O remetente, sem saber disso, faz o que foi programado para fazer. Se você está se perguntando por que as tentativas de reenvio de webhooks estão causando mensagens duplicadas, essa é uma das respostas mais comuns.
Verifique o caminho do código depois do efeito colateral, não só antes dele. O handler gravou no banco e depois falhou numa chamada de métricas? Publicou em uma fila e depois estourou timeout ao formatar JSON? Enviou um e-mail de saída e depois lançou um erro de validação? Pequenas falhas podem gerar grandes efeitos duplicados.
Como impedir duplicatas geradas por retries sem perder eventos?
Idempotência é a resposta prática. Armazene uma chave única para cada evento e rejeite repetições antes que possam disparar uma segunda ação. Essa chave pode ser o ID do evento, um ID de entrega do provedor ou uma combinação de origem e hash da mensagem. O importante é que o mesmo webhook resulte no mesmo desfecho armazenado.
Faça a checagem de deduplicação e a gravação em um único passo atômico, se possível. Se a verificação acontecer antes e o insert depois, dois workers podem correr ao mesmo tempo e ambos acreditarem que chegaram primeiro. Uma constraint única no banco costuma ser mais simples do que caches de memória personalizados, e sobrevive a reinícios. Pequenas escolhas de arquitetura importam aqui.
Use uma janela curta de deduplicação apenas quando a política de retries do remetente for curta e bem conhecida. Se o provedor tenta por horas, um cache de 5 minutos não basta. Se o evento pode chegar dias depois, o armazenamento de dedupe precisa durar mais do que a sua memória do incidente.
Para equipes que lidam tanto com entrega de eventos quanto com comunicação com clientes, o guia de configuração de autenticação de e-mail é um bom complemento, porque a mesma lógica se aplica: confirme a identidade primeiro, aja depois, e não confie em um único sinal isoladamente.
Envie a resposta 2xx somente depois que o evento estiver gravado com segurança. Isso não significa que todo o fluxo de negócio precise terminar antes do acknowledgement. Significa que o receptor deve preservar o evento em um local durável e depois processá-lo de forma assíncrona. Assim, o remetente vê sucesso uma vez, enquanto o seu worker pode tentar com segurança sem aceitar o mesmo evento novamente.
Quando você deve aumentar a visibilidade sobre tentativas repetidas de webhook?
Registre tentativas repetidas desde o primeiro dia. Não espere uma reclamação de usuário. Uma linha de log simples com ID do evento, ID de entrega, número da tentativa, código de resposta e tempo de processamento pode revelar uma tempestade de retries antes que ela se espalhe para e-mails, faturas ou mensagens de texto. Um alerta no momento certo vale mais do que dez tickets de suporte depois.
Defina um alerta quando o mesmo ID de evento aparecer mais de uma vez em uma janela curta, ou quando uma única origem disparar um pico de retries depois de um release. Esse pico pode significar timeouts, certificados expirados, um deploy com problema ou uma dependência downstream que ficou lenta o bastante para disparar retries. Quanto mais cedo você enxergar o padrão, menos mensagens duplicadas terá de limpar.
Mantenha um olho no receptor e outro no remetente. Se o painel do provedor mostra 7 tentativas, mas os logs do seu app mostram apenas 2, as 5 restantes podem ser falhas de rede. Se os logs do seu app mostram 7 eventos aceitos para um único ID, o problema está dentro do seu caminho de dedupe. De qualquer forma, o número indica onde olhar a seguir.
E se as duplicatas estiverem ligadas a alertas ou notificações, e não apenas aos webhooks, a mesma disciplina se aplica ao messaging downstream, especialmente quando um segundo sistema reage ao primeiro sem verificar se a primeira mensagem já foi processada.
Nesta página
← Todos os artigosUm clique. Ele nos diz o que escrever a seguir.
Nenhuma avaliação ainda — a sua seria a primeira.
Comentários
Os comentários são lidos antes de aparecerem.