Webhooks¶
Los webhooks de FrontEngine entregan notificaciones en tiempo real sobre eventos de email a su aplicación. Utilícelos para rastrear entregas, gestionar rebotes y medir el engagement.
Descripción general¶
Cuando ocurre un evento (email entregado, rebotado, abierto, etc.), FrontEngine envía una solicitud HTTP POST a la URL de webhook configurada con un payload JSON que describe el evento.
Eventos soportados¶
Eventos de mensaje:
| Evento | Disparador |
|---|---|
MessageSent | Mensaje enviado con éxito a un destinatario |
MessageDelayed | La entrega falló temporalmente, se reintentará |
MessageDeliveryFailed | El mensaje no se pudo entregar (fallo permanente) |
MessageHeld | El mensaje fue retenido para revisión |
MessageBounced | Se recibió una notificación de rebote para un mensaje previamente aceptado |
MessageLinkClicked | Un destinatario hizo clic en un enlace del email |
Eventos de servidor:
| Evento | Disparador |
|---|---|
SendLimitApproaching | El servidor ha alcanzado el 90% de la capacidad de envío |
SendLimitExceeded | El servidor ha excedido su límite de envío |
DomainDNSError | Falló la validación DNS de SPF, DKIM, MX o return path para un dominio |
Configuración¶
Configurando un webhook¶
- Vaya a Servicios > Email Transaccional > Webhooks
- Haga clic en Agregar Webhook
- Configure:
- URL: Su endpoint HTTPS (p. ej.,
https://app.example.com/webhooks/email) - Eventos: Seleccione qué eventos recibir
- Secreto: Un secreto compartido para la verificación del payload
- URL: Su endpoint HTTPS (p. ej.,
- Haga clic en Guardar
HTTPS requerido
Las URLs de webhook deben utilizar HTTPS. Los endpoints HTTP no están admitidos por razones de seguridad.
Verificación de firmas de webhook¶
Cada solicitud de webhook incluye un encabezado de firma para la verificación del payload:
Verifique la firma en su aplicación:
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 del payload¶
Todos los payloads de webhook son JSON. La estructura varía según el tipo de evento.
Payload de 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 de MessageBounced¶
Incluye tanto el mensaje original como los detalles del rebote:
{
"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 de 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 de 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 de 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"
}
}
}
Mejores prácticas¶
Responda rápidamente¶
Su endpoint debe devolver un estado 200 en 5 segundos. Procese los datos del webhook de forma asíncrona — encole el payload y devuelva inmediatamente.
@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)
# Enqueue for async processing
await queue.put(payload)
return Response(status_code=200)
Manejo de reintentos¶
Si su endpoint devuelve un estado distinto de 2xx o se agota el tiempo de espera, FrontEngine reintenta con backoff exponencial:
| Intento | Retraso |
|---|---|
| 1.er reintento | 1 minuto |
| 2.º reintento | 5 minutos |
| 3.er reintento | 30 minutos |
| 4.º reintento | 2 horas |
| 5.º reintento | 12 horas |
Tras 5 intentos fallidos, el webhook se marca como fallido. Puede ver y reenviar los webhooks fallidos en el portal.
Deduplicación de eventos¶
Las entregas de webhook son al menos una vez. Su endpoint puede recibir el mismo evento más de una vez. Utilice los campos message_id y event para deduplicar.
Idempotencia
Diseñe su controlador de webhook para que sea idempotente — procesar el mismo evento dos veces debe producir el mismo resultado que procesarlo una vez.
Pruebas de webhooks¶
Utilice el botón Probar en el portal para enviar un evento de ejemplo a su endpoint. También puede utilizar herramientas como webhook.site durante el desarrollo para inspeccionar los payloads.
Próximos pasos¶
- Referencia de API — Envíe mensajes mediante la API REST
- Autenticación — Configure SPF, DKIM y DMARC