Saltar a contenido

Webhooks

Os webhooks do FrontEngine entregam notificações em tempo real sobre eventos de email para sua aplicação. Use-os para rastrear entregas, lidar com rejeições e medir engajamento.

Visão Geral

Quando um evento ocorre (email entregue, rejeitado, aberto, etc.), o FrontEngine envia uma requisição HTTP POST para a URL de webhook configurada com um payload JSON descrevendo o evento.

Eventos Suportados

Eventos de Mensagem:

Evento Gatilho
MessageSent Mensagem enviada com sucesso para um destinatário
MessageDelayed Entrega falhou temporariamente, será tentada novamente
MessageDeliveryFailed Mensagem não pôde ser entregue (falha permanente)
MessageHeld Mensagem foi retida para análise
MessageBounced Uma notificação de rejeição foi recebida para uma mensagem previamente aceita
MessageLinkClicked Um destinatário clicou em um link no email

Eventos do Servidor:

Evento Gatilho
SendLimitApproaching O servidor atingiu 90% da capacidade de envio
SendLimitExceeded O servidor excedeu seu limite de envio
DomainDNSError A validação de DNS de SPF, DKIM, MX ou return path falhou para um domínio

Configuração

Configurando um Webhook

  1. Vá em Serviços > Email Transacional > Webhooks
  2. Clique em Adicionar Webhook
  3. Configure:
    • URL: Seu endpoint HTTPS (ex.: https://app.example.com/webhooks/email)
    • Eventos: Selecione quais eventos deseja receber
    • Segredo: Um segredo compartilhado para verificação do payload
  4. Clique em Salvar

HTTPS Obrigatório

As URLs de webhook devem usar HTTPS. Endpoints HTTP não são suportados por motivos de segurança.

Verificando Assinaturas de Webhook

Cada requisição de webhook inclui um cabeçalho de assinatura para verificação do payload:

X-Webhook-Signature: sha256=a1b2c3d4e5f6...

Verifique a assinatura na sua aplicação:

import hashlib
import hmac

def verify_webhook(payload: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(),
        payload,
        hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(f"sha256={expected}", signature)

Formato do Payload

Todos os payloads de webhook são JSON. A estrutura varia conforme o tipo de evento.

Payload MessageSent

{
  "event": "MessageSent",
  "payload": {
    "status": "sent",
    "details": "Message accepted by remote server",
    "output": "250 OK",
    "time": 1.23,
    "sent_with_ssl": true,
    "timestamp": 1711806600,
    "message": {
      "id": 12345,
      "token": "abc123def456",
      "direction": "outgoing",
      "message_id": "<[email protected]>",
      "to": "[email protected]",
      "from": "[email protected]",
      "subject": "Order Confirmation #12345",
      "timestamp": 1711806590,
      "spam_status": "NotSpam",
      "tag": "order-confirmation"
    }
  }
}

Payload MessageBounced

Inclui tanto a mensagem original quanto os detalhes da rejeição:

{
  "event": "MessageBounced",
  "payload": {
    "original_message": {
      "id": 12345,
      "token": "abc123def456",
      "to": "[email protected]",
      "from": "[email protected]",
      "subject": "Order Confirmation"
    },
    "bounce": {
      "id": 67890,
      "token": "xyz789",
      "to": "[email protected]",
      "from": "[email protected]",
      "subject": "Undelivered Mail Returned to Sender"
    }
  }
}

Payload MessageLinkClicked

{
  "event": "MessageLinkClicked",
  "payload": {
    "url": "https://example.com/track-order/12345",
    "token": "abc123def456",
    "ip_address": "198.51.100.42",
    "user_agent": "Mozilla/5.0...",
    "message": {
      "id": 12345,
      "token": "abc123def456",
      "to": "[email protected]",
      "from": "[email protected]",
      "subject": "Order Confirmation #12345",
      "tag": "order-confirmation"
    }
  }
}

Payload SendLimitApproaching / SendLimitExceeded

{
  "event": "SendLimitApproaching",
  "payload": {
    "server": {
      "uuid": "server-uuid",
      "name": "My Mail Server",
      "permalink": "my-mail-server",
      "organization": "My Organization"
    },
    "volume": 9000,
    "limit": 10000
  }
}

Payload DomainDNSError

{
  "event": "DomainDNSError",
  "payload": {
    "domain": "example.com",
    "uuid": "domain-uuid",
    "dns_checked_at": "2026-03-30T14:00:00Z",
    "spf_status": "OK",
    "spf_error": null,
    "dkim_status": "Missing",
    "dkim_error": "No DKIM record found for selector",
    "mx_status": "OK",
    "mx_error": null,
    "return_path_status": "OK",
    "return_path_error": null,
    "server": {
      "uuid": "server-uuid",
      "name": "My Mail Server"
    }
  }
}

Boas Práticas

Responda Rapidamente

Seu endpoint deve retornar um status 200 em até 5 segundos. Processe os dados do webhook de forma assíncrona — enfileire o payload e retorne imediatamente.

@app.post("/webhooks/email")
async def handle_webhook(request: Request):
    payload = await request.body()
    signature = request.headers.get("X-Webhook-Signature", "")

    if not verify_webhook(payload, signature, WEBHOOK_SECRET):
        return Response(status_code=401)

    # Enfileira para processamento assíncrono
    await queue.put(payload)
    return Response(status_code=200)

Lide com Retentativas

Se seu endpoint retornar um status diferente de 2xx ou expirar, o FrontEngine tenta novamente com backoff exponencial:

Tentativa Intervalo
1a retentativa 1 minuto
2a retentativa 5 minutos
3a retentativa 30 minutos
4a retentativa 2 horas
5a retentativa 12 horas

Após 5 tentativas sem sucesso, o webhook é marcado como falho. Você pode visualizar e reenviar webhooks com falha no portal.

Deduplicação de Eventos

As entregas de webhook são do tipo pelo menos uma vez. Seu endpoint pode receber o mesmo evento mais de uma vez. Use os campos message_id e event para deduplicação.

Idempotência

Projete seu handler de webhook para ser idempotente — processar o mesmo evento duas vezes deve produzir o mesmo resultado que processá-lo uma vez.

Testando Webhooks

Use o botão Testar no portal para enviar um evento de exemplo para seu endpoint. Você também pode usar ferramentas como webhook.site durante o desenvolvimento para inspecionar os payloads.

Próximos Passos