Engenharia · 21 de setembro de 2026

Chaves de idempotência para APIs de e-mail

Evite e-mails duplicados durante novas tentativas de rede implementando chaves de idempotência. Aprenda a lidar com falhas de sistemas distribuídos sem fazer spam com seus usuários.

O problema dos e-mails duplicados

E-mails duplicados acontecem quando um cliente envia uma requisição, o servidor a processa, mas a rede falha antes de o cliente receber a resposta de sucesso. O cliente, vendo um timeout ou um erro 5xx, tenta a requisição novamente. Sem idempotência, o servidor trata a nova tentativa como uma requisição nova e envia o e-mail outra vez. As chaves de idempotência evitam isso ao permitir que o servidor reconheça uma requisição repetida e retorne o resultado original sem executar o efeito colateral de novo.

Para um engenheiro responsável pela fila de incidentes, não há nada pior do que uma "tempestade de e-mails duplicados". Isso costuma acontecer durante uma indisponibilidade parcial de um provedor upstream ou um deadlock no banco de dados que deixa os tempos de resposta lentos. Sua lógica de novas tentativas, criada para dar confiabilidade, vira uma arma que faz spam com seus usuários e prejudica sua reputação de remetente.

Por que as novas tentativas falham sem idempotência

Em um sistema distribuído, há três pontos de falha para qualquer chamada de API:

  1. A requisição nunca chega ao servidor.
  2. O servidor processa a requisição, mas a resposta se perde.
  3. O servidor cai no meio do processamento.

Se você tentar novamente no caso 1, está seguro. No caso 2, você envia uma duplicata. No caso 3, você pode enviar uma duplicata, dependendo de onde ocorreu a queda.

Enviar um e-mail é um efeito colateral externo. Diferente de atualizar o nome de um usuário no banco de dados (o que é naturalmente idempotente se você usar SET name = 'Alice'), enviar um e-mail é uma ação cumulativa. Cada chamada a um endpoint send cria uma nova mensagem no mundo. Para tornar isso idempotente, você precisa introduzir um identificador único para a intenção de envio, conhecido como chave de idempotência.

Implementando chaves de idempotência

Uma chave de idempotência é um valor único (normalmente um UUID v4) gerado pelo cliente e enviado no cabeçalho da requisição. O servidor usa essa chave para acompanhar o estado da requisição.

O fluxo no servidor

  1. Receber a requisição: o servidor verifica se o cabeçalho Idempotency-Key existe.
  2. Consulta: o servidor procura essa chave em um armazenamento de acesso rápido (como o Redis).
  3. Cache hit: se a chave existir, o servidor retorna imediatamente a resposta em cache, sem acionar o mecanismo de entrega de e-mail.
  4. Cache miss: o servidor bloqueia a chave, processa o envio do e-mail, armazena a resposta e a retorna ao cliente.
  5. Expiração: a chave expira depois de uma janela (por exemplo, 24 horas) para evitar que o banco de dados cresça indefinidamente.

Exemplo concreto de payload

Veja como deve ficar uma requisição ao usar uma API como o SendHQ:

POST /v1/send Host: api.sendhq.cc Content-Type: application/json Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000 Authorization: Bearer YOUR_API_KEY { "to": "user@example.com", "template_id": "welcome-email", "variables": { "name": "Alex" } }

Tratando casos de erro

Nem todas as novas tentativas devem ser tratadas da mesma forma. Você precisa distinguir erros do cliente de erros do servidor.

  • Erros 4xx: se o servidor retornar 400 (Bad Request) ou 422 (Unprocessable Entity), a requisição é inválida. Tentar novamente com a mesma chave deve retornar o mesmo erro 4xx. Não altere o payload reutilizando a chave, pois isso cria um conflito.
  • Erros 5xx: se o servidor retornar 500 ou 503, o cliente deve tentar novamente. Se o servidor já tiver entregue o e-mail com sucesso ao MTA (Mail Transfer Agent), a chave de idempotência garante que a nova tentativa retorne 200 OK em vez de enviar um segundo e-mail.
  • Requisições concorrentes: se duas requisições idênticas com a mesma chave chegarem exatamente no mesmo milissegundo, o servidor deve retornar 409 Conflict para a segunda, indicando que a primeira ainda está em processamento.

Idempotência para agentes de IA

Agentes de IA (usando servidores MCP ou cards A2A) trazem uma nova camada de risco. LLMs podem ser não determinísticos e disparar a mesma chamada de ferramenta várias vezes se perceberem uma falha no loop.

Ao construir integrações prontas para agentes, nunca permita que um agente dispare uma ação send sem uma etapa de aprovação ou uma chave de idempotência determinística gerada pelo orquestrador. O orquestrador deve mapear a intenção do agente (por exemplo, "Envie o relatório semanal para o Bob") para uma chave estável baseada no ID do relatório e na data. Isso evita que o agente envie por engano o mesmo relatório cinco vezes porque "achou" que a primeira chamada falhou.

O custo da falha: comparação de provedores

Quando você deixa de implementar idempotência, não só irrita os usuários; também desperdiça dinheiro. Embora alguns provedores sejam mais baratos, o custo das duplicatas cresce rápido.

De acordo com as páginas de preços oficiais (em setembro de 2026):

  • Amazon SES: custa 0.10 USD por 1.000 e-mails no modelo a la carte (preços do Amazon SES). Os novos planos em níveis, lançados em 21 de julho de 2026, incluem Essentials (0.16 USD por 1.000), Pro (0.22 USD por 1.000 mais 105 USD por mês por região) e Enterprise (0.23 USD por 1.000 mais 500 USD por mês).
  • Resend: o plano gratuito oferece 3.000 e-mails por mês, limitado a 100 por dia. O Pro custa 20 USD por mês para 50.000 e-mails, com excedente de 0.90 USD por 1.000 (preços do Resend).
  • SendGrid: o plano gratuito agora é um teste de 60 dias, e os planos Essentials começam em 19.95 USD por mês (preços do SendGrid).
  • Mailgun: custa 15 USD por mês para 10.000 e-mails, com excedente variando de 1.80 a 1.10 USD por 1.000 (preços do Mailgun).
  • Postmark: custa 15 USD por mês para 10.000 e-mails, com excedente de 1.80 a 1.20 USD por 1.000 (preços do Postmark).

Para colocar em perspectiva: enviar 50.000 e-mails custa cerca de 5 USD no SES a la carte, contra cerca de 66 USD nos planos do Postmark. Se um loop de novas tentativas sem idempotência multiplicar seu volume por 10x, a diferença financeira entre provedores vira um item relevante no seu relatório de incidente.

Entregabilidade vs. aceitação

É fundamental entender que a idempotência resolve apenas o problema da aceitação.

  1. Aceitação: a API aceita sua requisição e retorna 200 OK. É aqui que as chaves de idempotência atuam.
  2. Entrega: a API entrega o e-mail ao servidor de destino (por exemplo, o Gmail). É aqui que SPF e DKIM/DMARC importam.
  3. Chegada à caixa de entrada: o servidor de destino decide se o e-mail vai para a caixa de entrada ou para a pasta de spam.

Uma chave de idempotência garante que você só aceite a requisição uma vez. Ela não garante que o e-mail seja entregue nem que escape da pasta de spam. Para garantir que sua infraestrutura esteja configurada corretamente para a entrega, use ferramentas como o Verificador de DNS de e-mail do SendHQ para conferir seus registros.

Lista de verificação de implementação para engenheiros

Se você for auditar sua lógica de envio de e-mail hoje, use esta lista:

  • Geração de chaves no cliente: Você gera um UUID v4 para cada intenção de e-mail única?
  • Implementação do cabeçalho: A chave é enviada em um cabeçalho padrão (por exemplo, Idempotency-Key) em vez de no corpo da requisição?
  • Camada de armazenamento: Suas chaves de idempotência têm TTL (Time To Live) para evitar o crescimento excessivo do armazenamento?
  • Bloqueio atômico: Seu servidor usa um lock distribuído (como SET NX no Redis) para evitar condições de corrida na mesma chave?
  • Cache de respostas: Você armazena a resposta completa (código de status e corpo) para devolvê-la ao cliente em novas tentativas?
  • Proteções para agentes: Ao usar agentes de IA, a chave é gerada pelo orquestrador do sistema em vez do LLM?

Exemplo de código: middleware de idempotência em Node.js

Veja um exemplo simplificado de como implementar essa lógica em um ambiente Node.js usando Redis.

const redis = require('redis'); const client = redis.createClient(); async function sendEmailHandler(req, res) { const idempotencyKey = req.headers['idempotency-key']; if (!idempotencyKey) { return res.status(400).json({ error: 'Idempotency-Key header is required' }); } // Try to acquire a lock and check for existing response const cachedResponse = await client.get(`idempotency:${idempotencyKey}`); if (cachedResponse) { const { status, body } = JSON.parse(cachedResponse); return res.status(status).json(body); } // Set a lock to prevent concurrent requests const lock = await client.set(`lock:${idempotencyKey}`, 'true', 'NX', 'EX', 30); if (!lock) { return res.status(409).json({ error: 'Request is currently being processed' }); } try { // Actual email sending logic const result = await emailProvider.send(req.body); const responsePayload = { status: 200, body: result }; // Cache the result for 24 hours await client.set(`idempotency:${idempotencyKey}`, JSON.stringify(responsePayload), 'EX', 86400); return res.status(200).json(result); } catch (error) { return res.status(500).json({ error: 'Internal Server Error' }); } finally { await client.del(`lock:${idempotencyKey}`); } }

Considerações finais

Idempotência não é um "extra desejável" no e-mail transacional; é um requisito para qualquer sistema que valorize a experiência do usuário e o controle de custos. Ao transferir para o cliente a responsabilidade pela unicidade e oferecer no servidor um mecanismo para rastreá-la, você elimina o risco de envios duplicados durante instabilidades de rede.

Seja construindo um produto SaaS tradicional ou um agente de IA autônomo, tratar o e-mail como um efeito colateral crítico mantém seu sistema confiável e seus usuários satisfeitos. Para uma API de e-mail feita para desenvolvedores que cuida dessas complexidades, conheça https://sendhq.cc.