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¶
- Vá em Serviços > Email Transacional > Webhooks
- Clique em Adicionar Webhook
- 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
- URL: Seu endpoint HTTPS (ex.:
- 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:
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¶
- Referência da API — Envie mensagens via API REST
- Autenticação — Configure SPF, DKIM e DMARC