Eventos de email
Emitimos eventos a medida que cada destinatario avanza por el proceso de entrega. Un envío a tres direcciones produce tres flujos independientes, correlacionados por email_id y recipient_id. Esta página define los tipos de eventos de email. Consulta Webhooks para firmas, reintentos, orden y reproducción.
Cada destinatario comienza en email.accepted y luego email.processed. Un destinatario de broadcast es su propio mensaje, así que obtiene su propio email.accepted también, aunque solo en los eventos API y el registro de email, no como webhook. Desde ahí el mensaje es aceptado por el servidor receptor (email.delivered), se aplaza y reintenta (email.deferred, que se resuelve como entregado o rebotado), es rechazado por el servidor receptor (email.bounced), o nunca llega a intentarse la entrega (email.rejected). Después de una entrega, el flujo puede continuar con email.out_of_band_bounce, email.complained, email.opened, email.clicked, email.unsubscribed y email.list_unsubscribed.
Cada destinatario termina en exactamente un estado terminal: delivered, bounced, complained o rejected, devuelto como el status por destinatario de GET /v1/email/messages/{message_id}/recipients. Los eventos de interacción nunca lo cambian: un destinatario que abrió el mensaje sigue siendo delivered. Un rebote tardío sí lo cambia, porque el servidor receptor está retractando una aceptación que ya dio, y el destinatario pasa de delivered a bounced. El mensaje en su conjunto tiene su propio estado consolidado y conteos por estado en GET /v1/email/messages/{message_id}.
El sobre del evento
Los eventos llegan en el sobre de tres campos que usa todo webhook: type, timestamp (cuándo ocurrió el evento, RFC 3339) y un objeto data específico del tipo.
Ejemplo de código
{
"type": "email.delivered",
"timestamp": "2026-07-23T14:51:47.107Z",
"data": {
"email_id": "em_01ky7qc398fmxraqtxn604zeq9",
"recipient_id": "er_01ky7qc398fmwsc96nt6anrbqs",
"workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf",
"recipient": "delivered@messagebird.dev",
"recipient_role": "to",
"tags": [{ "name": "category", "value": "welcome" }],
"metadata": { "order_id": "ord_123" },
"broadcast_id": null
}
}Todo evento saliente incluye email_id, recipient_id, workspace_id, la dirección recipient y su recipient_role de sobre (to, cc o bcc). También repite tags y metadata de la solicitud de envío para que puedas correlacionar el evento con tus registros. Cada valor opcional es null cuando el envío no tenía ninguno, incluido broadcast_id: nombra el broadcast del que formó parte un envío, para que puedas agrupar los eventos de un broadcast sin consultar cada envío, y es null en un envío sin broadcast detrás. Un caso reporta null para un envío que sí tenía uno: un enlace de cancelación de suscripción de correo enviado antes de que añadiéramos el campo no nombra ningún broadcast, así que una baja a través de ese enlace reporta null en email.unsubscribed y email.list_unsubscribed independientemente de si un broadcast envió el correo. Trata null en esos dos eventos como no concluyente, o subestimarás las bajas de un broadcast. broadcast_id te llega solo en el webhook: los eventos API de abajo devuelven cada evento sin él. Los tipos de evento añaden campos descritos en las secciones de ciclo de vida, interacción, supresión y entrada.
Los mismos eventos son consultables después del hecho desde GET /v1/email/messages/{message_id}/events, donde cada uno tiene además un id (prefijo ev_) y un occurred_at. Úsalo para rellenar, reproducir o conciliar con lo que recibió tu endpoint. Algunos campos te llegan solo a través de ese API en lugar del webhook; la descripción del evento correspondiente identifica cada uno.
Eventos de ciclo de vida
email.accepted
Hemos aceptado el envío y comenzado a preparar la entrega. Se dispara una vez por destinatario solicitado y es el primer evento en ese flujo. Un destinatario de broadcast también recibe uno, porque cada destinatario es su propio mensaje, pero se registra en lugar de entregarse: léelo desde los eventos API o el registro de email, no desde tu endpoint de webhook. Payload: solo la base de identidad.
email.processed
El mensaje está construido y en cola para entrega al servidor de correo del destinatario. Payload: solo la base de identidad por webhook; los eventos API añaden mailbox_provider y mailbox_provider_region, la clasificación del sistema de correo receptor (por ejemplo gmail, NA), presente cuando pudo determinarse y null en caso contrario. Comparar el timestamp de este evento con el de email.accepted te da nuestro tiempo de procesamiento en un envío individual. Un broadcast no tiene ese intervalo: su aceptación y su procesamiento llevan el mismo instante de despacho, así que las dos marcas de tiempo coinciden en lugar de delimitar algún procesamiento, y la aceptación te llega solo a través de los eventos API, como occurred_at.
email.delivered
El servidor de correo receptor aceptó el mensaje y asumió la responsabilidad de entregarlo. Este evento no confirma la ubicación en bandeja de entrada ni la lectura. Inbox Insights proporciona estimaciones de ubicación por muestreo; los eventos de apertura y clic registran las solicitudes de seguimiento. Payload: solo la base de identidad por webhook; los eventos API añaden sending_ip, la dirección desde la que se envió el mensaje, relevante cuando un problema de entregabilidad apunta a una IP, más mailbox_provider y mailbox_provider_region.
email.deferred
Un fallo temporal: el servidor receptor nos pidió que reintentáramos más tarde (buzón lleno, greylisting, limitación de solicitudes). Reintentamos automáticamente, y el destinatario finalmente se resuelve como email.delivered o email.bounced, así que este evento es informativo y no terminal, y un destinatario puede aplazarse varias veces antes. Payload: bounce_type, bounce_class, defer_reason (el motivo que dio el servidor) y sending_ip por webhook; los eventos API añaden mailbox_provider y mailbox_provider_region.
Eventos de fallo
email.bounced
Un fallo permanente en el momento de SMTP: el servidor receptor rechazó el mensaje y el estado terminal del destinatario pasa a bounced. Payload: bounce_type (consulta la tabla de clasificación), bounce_class, bounce_code (el código de respuesta SMTP, por ejemplo 550), bounce_description (el motivo que dio el servidor) y sending_ip por webhook; los eventos API añaden mailbox_provider y mailbox_provider_region. Un rebote duro suprime la dirección.
email.out_of_band_bounce
Un rebote tardío: el servidor receptor aceptó el mensaje en el momento de SMTP y luego envió un informe de rebote. Tiene la misma clasificación que email.bounced (bounce_type, bounce_class, bounce_code, bounce_description, sending_ip por webhook; mailbox_provider y mailbox_provider_region desde los eventos API). Cuando el informe se clasifica como rebote (cualquier clase en la tabla), el servidor ha retractado su aceptación anterior y el destinatario pasa de delivered a bounced. Los informes cuya clase no está en la tabla, como las respuestas automáticas, se registran en la línea de tiempo y no modifican el estado. Un rebote tardío duro también suprime la dirección.
email.rejected
El destinatario nunca llegó al servidor de correo remoto, así que no se intentó ninguna entrega. Eso es lo que separa un rechazo de un rebote, donde es el servidor receptor el que dice no. Payload: rejection_reason, también en el registro del destinatario, uno de:
| rejection_reason | Significado |
|---|---|
| recipient_suppressed | El destinatario está bloqueado a nivel de espacio de trabajo, por la lista de supresión o por una preferencia declarada, así que la entrega nunca se intentó |
| transmission_failed | El mensaje no pudo transmitirse para su entrega |
| generation_failure | El mensaje no pudo construirse para su entrega, un problema de plantilla o contenido |
| policy_rejection | La política de envío rechazó el mensaje |
| domain_unverified | El dominio de envío no estaba verificado |
| quota_exceeded | Se alcanzó la cuota de envío de la organización |
| recipient_not_allowed | El destinatario no estaba permitido para este envío; los envíos en un dominio de incorporación compartido solo llegan a miembros verificados de tu espacio de trabajo |
Los eventos API también añaden mailbox_provider y mailbox_provider_region cuando el sistema de correo receptor pudo clasificarse antes del rechazo.
email.complained
El destinatario marcó el mensaje como spam y el proveedor de buzón lo reportó a través de su bucle de retroalimentación. Las quejas llegan después de la entrega y fijan el estado terminal en complained. Payload: feedback_type, el tipo de informe que envió el proveedor, como abuse o fraud, y null cuando el proveedor no lo indicó, más mailbox_provider y mailbox_provider_region desde los eventos API. Una queja suprime la dirección para correo de marketing. Mantén baja tu tasa de quejas: los proveedores limitan a los remitentes que acumulan reportes.
Eventos de interacción
email.opened
El píxel de seguimiento en el cuerpo del mensaje se cargó. Payload: ip_address y user_agent cuando se conocen; los eventos API añaden is_prefetched, country (ISO 3166-1 alpha-2, derivado de la IP del cliente), mailbox_provider y mailbox_provider_region. Verifica is_prefetched antes de contar una apertura. Es true cuando una función de privacidad de la bandeja de entrada cargó el píxel automáticamente en lugar de una persona abriendo el mensaje, y contar esas infla tu tasa de apertura. Seguimiento de aperturas y clics cubre la instrumentación.
email.clicked
El destinatario hizo clic en un enlace con seguimiento. Payload: url (el enlace en el que se hizo clic), ip_address y user_agent cuando se conocen; los eventos API añaden country, mailbox_provider y mailbox_provider_region. Los clics son generalmente una señal de interacción más fuerte que las aperturas, porque los proxies de privacidad pueden cargar píxeles de seguimiento automáticamente.
email.unsubscribed
El destinatario usó el enlace de cancelación de suscripción en el cuerpo del mensaje. Payload: solo la base de identidad por webhook; los eventos API añaden mailbox_provider y mailbox_provider_region. Registra una preferencia de baja que bloquea el correo de marketing. Enlaces de cancelación de suscripción cubre cómo el enlace llega a tu correo.
email.list_unsubscribed
El destinatario usó el botón de cancelación de suscripción con un clic que el proveedor de buzón muestra en su propia interfaz, basado en las cabeceras List-Unsubscribe del mensaje. Payload: solo la base de identidad por webhook (más mailbox_provider y mailbox_provider_region desde los eventos API); el mecanismo es el tipo de evento en sí, por eso está separado de email.unsubscribed. También registra una preferencia de baja que bloquea el correo de marketing.
Eventos a nivel de mensaje
Dos eventos describen el mensaje en su conjunto en lugar de un destinatario, así que su data tiene email_id, workspace_id, tags y metadata pero no la identidad del destinatario. Ambos pertenecen al envío programado.
email.scheduled
Aceptamos un envío con un scheduled_at en el futuro. Payload: la base a nivel de mensaje más scheduled_at. Cuando llega ese momento, el ciclo de vida por destinatario comienza en email.accepted.
email.canceled
Un mensaje programado fue cancelado antes de enviarse, así que no produce ningún evento de ciclo de vida de destinatario. Payload: solo la base a nivel de mensaje.
Eventos de entrada y buzón
email.received cubre el correo entrante. Se dispara cuando recibimos y parseamos un mensaje entrante. Su payload incluye el inbound_message_id, direccionamiento, asunto y veredictos de autenticación. La configuración, el payload y el API de consulta están en Recepción de email. Un buzón tiene su propia familia email_mailbox.* adicional, cubierta en la guía de buzones.
Clasificación de rebotes
bounce_class es la clasificación numérica de rebote incluida en email.bounced, email.out_of_band_bounce y email.deferred. Se agrupa en la categoría general bounce_type y conserva el código detallado, para que puedas distinguir un buzón lleno de un fallo de enrutamiento aunque ambos reporten como soft:
| bounce_class | bounce_type | Significado |
|---|---|---|
| 1 | undetermined | La respuesta del servidor receptor fue ambigua |
| 10, 30 | hard | Fallo permanente: dirección no válida o dominio inexistente |
| 20 to 24, 40, 70, 100 | soft | Fallo transitorio: buzón lleno, servidor temporalmente no disponible, problema de DNS o enrutamiento |
| 25 | admin | Rechazo administrativo: retransmisión denegada, dominio en lista de bloqueo |
| 50 to 54 | block | El servidor receptor rechazó la IP de envío |
Cualquier clase fuera de esta lista se asigna a undetermined. Solo los rebotes hard suprimen la dirección; soft, block, admin y undetermined no, porque la dirección aún puede ser entregable.
Supresión automática
Dos eventos añaden un destinatario a la lista de supresión del espacio de trabajo automáticamente, y bloquean correo diferente:
| Evento | Supresión reason | Qué bloquea |
|---|---|---|
| email.bounced o email.out_of_band_bounce con bounce_type: "hard" | hard_bounce | Todo el correo, incluido el transaccional |
| email.complained | complaint | Correo de marketing; el transaccional sigue enviándose |
Un rebote duro bloquea todo porque la dirección ya no existe. Una queja bloquea solo marketing, porque alguien que reportó tu boletín como spam aún necesita su restablecimiento de contraseña.
email.unsubscribed y email.list_unsubscribed bloquean correo de la misma forma que una queja, solo marketing, pero a través de un registro diferente: en lugar de añadir una supresión, registran la baja del destinatario como preferencia declarada. Qué hace una baja cubre ese registro en detalle.
Cada adición dispara un evento email_suppression.created que tiene el suppression_id, la email suprimida, el reason y el workspace_id. El esquema completo del registro y cómo gestionar entradas manualmente están en la Guía de supresiones.
Los envíos posteriores a una dirección suprimida se rechazan de entrada como email.rejected con rejection_reason: "recipient_suppressed", y nunca cuentan en contra de tu entregabilidad.
Próximos pasos
- Webhooks y eventos: configuración de endpoint, verificación de firma, reintentos y reproducción
- Supresiones: cómo funciona la lista de supresión y cómo gestionarla
- Enlaces de cancelación de suscripción: cómo configurar las rutas detrás de email.unsubscribed y email.list_unsubscribed
- Testing y sandbox: los envíos de sandbox emiten eventos reales por la ruta normal, que es la forma más económica de probar tu handler
- Webhooks bien hechos: eventos de entrega fiables: un video que crea un webhook y observa cómo llegan los eventos
Recursos relacionados
Continúa con la documentación, guías y ejemplos sobre este tema. Los recursos están en inglés.
Ver la guíaGetting started with emailExplorar la funcionalidadEmailSeguir la ruta de aprendizajeBuild your first integrationGuía de implementaciónSend your first email
Prueba el ejercicio y obtén un resumen de implementación