Engenharia · 21 de setembro de 2026
Como projetar webhooks de e-mail para entrega at-least-once
Aprenda a construir consumidores de webhook resilientes para eventos de e-mail usando novas tentativas, chaves de idempotência e verificação de assinatura, para que nenhum evento de entrega se perca.
O desafio da entrega confiável de eventos
Para obter entrega pelo menos uma vez (at-least-once) em webhooks de e-mail, você precisa implementar um sistema em que o remetente tente novamente as requisições que falharam com backoff exponencial e o receptor garanta a idempotência. Como redes não são confiáveis e servidores caem, você não pode presumir que um único HTTP 200 OK garante que o evento foi processado. A confiabilidade vem da combinação de uma fila persistente de novas tentativas do lado do remetente com uma camada de deduplicação do lado do receptor.
Quando você integra uma API de e-mail como o SendHQ, sua aplicação precisa saber quando um e-mail foi entregue, gerou bounce ou foi marcado como spam. Esses eventos são assíncronos. Se o seu endpoint de webhook ficar fora do ar por cinco minutos durante um pico de tráfego, você pode perder milhares de sinais de entrega críticos. Isso cria uma lacuna nos seus dados de analytics e impede que seu sistema reaja aos bounces (o que é essencial para manter a reputação do remetente).
Anatomia de um webhook confiável
Uma arquitetura de webhook robusta se apoia em três pilares principais: verificação de assinatura, processamento idempotente e uma estratégia de novas tentativas.
1. Verificação de assinatura
Nunca confie em uma requisição POST ao seu endpoint de webhook apenas com base no endereço IP ou na presença de uma chave de API no corpo. Atacantes podem falsificar essas informações. Em vez disso, use uma assinatura HMAC (Hash-based Message Authentication Code).
O remetente assina o payload com um segredo compartilhado e anexa a assinatura a um cabeçalho (por exemplo, X-SendHQ-Signature). O receptor recalcula o hash com o mesmo segredo e o compara com o cabeçalho.
const crypto = require('crypto');
function verifySignature(payload, signature, secret) {
const expectedSignature = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
// Use timingSafeEqual to prevent timing attacks
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expectedSignature));
}
2. Idempotência e deduplicação
Entrega pelo menos uma vez significa que o remetente continuará enviando o evento até receber uma resposta de sucesso. Se o seu servidor processar o evento mas cair antes de enviar o 200 OK, o remetente enviará o evento de novo. Sem idempotência, você pode contar uma única entrega como duas no seu banco de dados.
Todo evento deve ter um event_id único. Use o padrão de chave de idempotência para registrar os eventos já processados.
O fluxo:
- Receba o payload do webhook.
- Verifique se o
event_idjá existe na sua tabelaprocessed_events. - Se existir, retorne 200 OK imediatamente e ignore o corpo.
- Se não existir, processe o evento e registre o
event_idem uma única transação.
3. A estratégia de novas tentativas
Do ponto de vista do remetente, uma política de novas tentativas é obrigatória. Um padrão comum é o backoff exponencial com jitter. Por exemplo: tentar novamente após 1 minuto, 5 minutos, 30 minutos, 2 horas e 12 horas.
Se o receptor retornar um erro 4xx (exceto 429), isso normalmente indica um erro do cliente (como uma assinatura inválida), e tentar novamente não vai ajudar. Um erro 5xx ou um timeout indica uma falha transitória, em que as novas tentativas são essenciais.
Exemplo concreto de payload
Veja um payload típico de evento de entrega que você pode receber do SendHQ:
{
"event_id": "evt_12345abcde",
"event_type": "delivered",
"timestamp": "2026-09-15T10:00:00Z",
"message_id": "msg_98765xyz",
"recipient": "user@example.com",
"metadata": {
"order_id": "ord_5544"
}
}
Tratando falhas e casos extremos
O problema do "consumidor lento"
Se o seu handler de webhook faz escritas pesadas no banco de dados ou chama outras APIs externas de forma síncrona, seu endpoint vai estourar o timeout. Isso aciona a lógica de novas tentativas do remetente e leva a uma "tempestade de novas tentativas" que pode derrubar seu servidor.
A solução: desacople o recebimento do processamento.
- Receba o webhook.
- Verifique a assinatura.
- Coloque o payload bruto em uma fila de mensagens (como RabbitMQ, SQS ou Redis).
- Retorne 200 OK imediatamente.
- Um processo worker separado consome a fila e atualiza seu banco de dados.
O problema da prontidão dos agentes
Quando agentes de IA são acionados por webhooks, o risco de loops infinitos aumenta. Se um agente recebe um evento "delivered" e responde enviando outro e-mail, que por sua vez gera outro evento "delivered", você tem um loop.
Trate o envio de e-mail como um efeito colateral externo. Agentes nunca devem enviar e-mails automaticamente com base em um webhook sem aprovação humana no circuito ou uma verificação rigorosa por máquina de estados que garanta que a ação é necessária.
Comparando o ecossistema
Ao escolher um provedor, a confiabilidade muitas vezes está ligada à forma como ele trata esses eventos e ao quanto cobra pelo volume de e-mails que gera esses eventos.
Para e-mail transacional de alto volume, a diferença de custo é gritante. De acordo com os preços do Amazon SES, o envio a la carte custa 0.10 USD por 1.000 e-mails. Em comparação, os preços do Postmark começam em 15 USD por mês para 10.000 e-mails, com excedente entre 1.20 e 1.80 USD por 1.000. Para um volume de 50.000 e-mails, o SES a la carte custa cerca de 5 USD, enquanto os planos do Postmark custariam aproximadamente 66 USD.
Outras opções incluem o Resend, que oferece um plano gratuito de 3.000 e-mails por mês (limitado a 100 por dia) e um plano Pro de 20 USD por mês para 50.000 e-mails. O Mailgun começa em 15 USD por mês para 10.000 e-mails. O SendGrid transformou seu plano gratuito em um teste de 60 dias, com o Essentials a partir de 19.95 USD por mês.
Independentemente do provedor, é a confiabilidade do seu consumo desses eventos que determina a integridade dos seus dados.
Lista de verificação de implementação para engenheiros
- Verificação de assinatura: O payload é verificado usando um segredo compartilhado e uma função de comparação em tempo constante?
- Processamento assíncrono: O endpoint retorna 200 OK antes de executar lógica de negócios pesada?
- Idempotência: Há uma restrição de unicidade em
event_idpara evitar processamento duplicado? - Gerenciamento de timeout: O timeout está configurado abaixo do timeout do provedor para evitar novas tentativas sobrepostas?
- Monitoramento: Você tem alertas para um pico de respostas 5xx no endpoint de webhook?
- Integridade do DNS: Seus servidores de recebimento estão configurados corretamente? Use ferramentas como o Verificador de DNS de e-mail do SendHQ para garantir que sua infraestrutura esteja acessível e corretamente configurada.
- Padrões de autenticação: Você implementou DKIM, SPF e DMARC para garantir que seus e-mails de saída sejam aceitos, reduzindo o número de webhooks de "bounce" que precisa processar?
Resumo dos trade-offs
Abordagem | Prós | Contras
Processamento síncrono | Simples de implementar, consistência imediata | Alto risco de timeouts, sujeito a tempestades de novas tentativas
Processamento baseado em fila | Altamente escalável, resiliente a picos | Mais complexidade de infraestrutura, consistência eventual
Log simples | Baixo custo | Não há como recuperar eventos perdidos sem logs manuais
Tabela de idempotência | Integridade de dados garantida | Uma escrita extra no banco de dados por evento
Considerações finais
Confiabilidade em webhooks de e-mail não é evitar falhas, e sim projetar para elas. Ao presumir que a rede vai falhar e que os eventos serão entregues mais de uma vez, você constrói um sistema realmente resiliente. Seja gerenciando registros SPF de um projeto pequeno ou escalando um sistema transacional enorme, os padrões de verificação de assinatura e idempotência continuam sendo a referência.
Construa sua infraestrutura de e-mail com o SendHQ.