Webhooks
Envía eventos de Bitelio a tus propios endpoints en tiempo real usando pasos Webhook de flujo de trabajo
Cuando algo ocurre en Bitelio — un correo rebota, llega una queja de spam, se dispara un evento personalizado — puedes hacer que una solicitud HTTP llegue a tu aplicación en tiempo real. El mecanismo es un flujo de trabajo que contiene un paso Webhook que envía los datos del evento a un endpoint que tú controlas.
¿Buscas una entrega firmada y con reintentos?
Esta página cubre el paso Webhook de flujo de trabajo: flexible, pero sin firma, sin historial de entregas, reenvío ni disyuntor de circuito, y sus reintentos solo cubren el paso en el que reentra un job reintentado (ver "Recibir webhooks de forma segura" más abajo). Para una alternativa gestionada centralmente — Bitelio firma cada petición, reintenta de extremo a extremo, y degrada un endpoint que sigue fallando — ver Webhooks, que se registran en Ajustes → Webhooks.
Cómo funciona
Los webhooks se apoyan sobre el sistema de flujos de trabajo. La cadena se ve así:
- Ocurre algún evento dentro de Bitelio (un correo rebota, un contacto se suscribe, se rastrea un evento personalizado, ...)
- Se dispara un flujo de trabajo que escucha ese evento
- Su paso Webhook se dispara, entregando una solicitud HTTP con los datos relevantes a tu URL
Como cualquier evento rastreado puede disparar un flujo de trabajo, cualquiera de ellos — generado por el sistema o tus propios eventos personalizados — puede llegarte como un webhook.
Eventos internos
Bitelio mismo rastrea un conjunto de eventos internos y los ofrece como disparadores de flujo de trabajo. No puedes registrar estos mediante la API; solo el sistema los emite.
Eventos de correo
| Evento | Descripción |
|---|---|
email.sent | Un correo se envió con éxito |
email.delivery | Un correo se entregó al destinatario |
email.open | Un contacto abrió un correo por primera vez |
email.click | Un contacto hizo clic en un enlace de un correo por primera vez |
email.bounce | Un correo rebotó (rebote duro o suave) |
email.complaint | Un contacto marcó un correo como spam |
email.received | Se recibió un correo en tu dominio verificado (requiere configuración de correo entrante) |
Eventos de contacto
| Evento | Descripción |
|---|---|
contact.subscribed | El estado de suscripción de un contacto cambió a suscrito |
contact.unsubscribed | El estado de suscripción de un contacto cambió a no suscrito |
Eventos de segmento
| Evento | Descripción |
|---|---|
segment.<name>.entry | Un contacto entró a un segmento |
segment.<name>.exit | Un contacto salió de un segmento |
Nombres de eventos de segmento
La parte <name> es el nombre del segmento en forma de slug. Un segmento llamado "VIP Users" por lo tanto emite
segment.vip-users.entry y segment.vip-users.exit.
Configurar un webhook
Crea el flujo de trabajo
Ve a Flujos de trabajo en el panel e inicia uno nuevo, eligiendo el evento que te interesa como su disparador. Para recibir notificaciones sobre rebotes, por ejemplo, establece el disparador en email.bounce.
Añade un paso Webhook
Adjunta un paso Webhook después del disparador y completa sus ajustes:
- URL: Dónde en tu servidor debe aterrizar el webhook (p. ej.
https://api.example.com/webhooks/bitelio) - Método: El verbo HTTP a usar.
POSTes el predeterminado y la elección correcta para la mayoría de situaciones. - Encabezados (opcional): Encabezados adicionales para la solicitud, dados como JSON — útil para autenticación.
{
"Authorization": "Bearer your-secret-token"
}- Cuerpo (opcional): Un cuerpo de solicitud personalizado. Déjalo vacío y Bitelio envía la carga útil por defecto documentada abajo; suministra uno y reemplaza por completo esa carga por defecto, codificada en JSON antes de enviarse.
Usa variables en la solicitud (opcional)
La interpolación {{variable}} funciona en la url, en los valores de encabezado, y en el body. El alcance de variables coincide con el que obtienen las plantillas SEND_EMAIL, con una adición exclusiva de los webhooks: un espacio de nombres event que lleva el payload del evento disparador:
| Variable | Valor |
|---|---|
{{id}}, {{email}} | El ID y el correo del contacto. |
{{<key>}} (nivel superior) | Cualquier clave del JSON data del contacto (p. ej. {{firstName}}, {{plan}}). |
{{data.<key>}} | Los mismos datos de contacto, dirigidos vía el espacio de nombres data. |
{{event.<key>}} | Exclusivo de webhooks. Campos del payload del evento disparador (p. ej. {{event.subject}}). |
{{<key>}} (del contexto de ejecución) | Claves pasadas como context al iniciar una ejecución MANUAL. |
{{unsubscribeUrl}}, {{subscribeUrl}}, {{manageUrl}} | URL de gestión de suscripción por contacto. |
Dos cosas no son parametrizables: el method HTTP, que tiene que ser un verbo literal (GET, POST, PUT, PATCH, DELETE), y el esquema de la URL, que debe escribirse como http:// o https:// — los marcadores pueden aparecer en cualquier otra parte de la URL, solo no en lugar del esquema.
Ejemplo — reenviar un evento de contacto a tu propia API, parametrizado con datos del contacto:
URL
https://api.example.com/users/{{id}}/eventsEncabezados
{
"Authorization": "Bearer your-secret-token"
}Cuerpo
{
"email": "{{email}}",
"plan": "{{plan}}",
"referrer": "{{event.referrer}}"
}Habilita el flujo de trabajo
Activa el flujo de trabajo terminado. A partir de ahí, cada ocurrencia del evento disparador produce una solicitud de webhook.
Carga útil del webhook
Sin ningún cuerpo personalizado configurado, la carga útil por defecto que envía Bitelio es una solicitud JSON con esta forma:
{
"contact": {
"email": "user@example.com",
"subscribed": true,
"data": {
"name": "John",
"plan": "pro"
}
},
"workflow": {
"id": "wf_abc123",
"name": "Bounce Notifications"
},
"execution": {
"id": "exec_xyz789",
"startedAt": "2025-01-15T10:30:00.000Z"
},
"event": {
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": "camp_abc123",
"sourceType": "CAMPAIGN",
"bounceType": "Permanent",
"bouncedAt": "2025-01-15T10:30:00.000Z"
}
}Cualquier dato que haya acompañado al evento disparador termina en el campo event — así que su forma exacta varía según el tipo de evento.
Datos de evento por tipo
Eventos de correo
Un conjunto común de campos base aparece en la mayoría de los eventos de correo:
Prop
Type
Además de esta base, cada tipo de evento añade sus propios campos, mostrados por pestaña abajo. Recuerda que los campos base (subject, from, fromName, messageId, emailId, templateId, campaignId, sourceType) acompañan a todo evento de correo junto a los específicos del evento.
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"sentAt": "2025-01-15T10:30:00.000Z"
}Prop
Type
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": "camp_abc123",
"sourceType": "CAMPAIGN",
"deliveredAt": "2025-01-15T10:30:05.000Z"
}Prop
Type
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"openedAt": "2025-01-15T11:00:00.000Z",
"opens": 1,
"isFirstOpen": true
}Prop
Type
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"link": "https://example.com/pricing",
"clickedAt": "2025-01-15T11:05:00.000Z",
"clicks": 1,
"isFirstClick": true
}Prop
Type
Rebote permanente:
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"bounceType": "Permanent",
"bouncedAt": "2025-01-15T10:31:00.000Z"
}Rebote transitorio (suave):
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"bounceType": "Transient",
"transientBounce": true
}Prop
Type
Impacto en la tasa de rebote
Solo los rebotes Permanent cuentan hacia la tasa de rebote de tu proyecto y disparan la baja automática del contacto.
Los rebotes Transient se rastrean solo para visibilidad.
{
"subject": "Welcome to Bitelio",
"from": "hello@example.com",
"fromName": "Bitelio Team",
"messageId": "ses-message-id",
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"templateId": null,
"campaignId": null,
"sourceType": "TRANSACTIONAL",
"complainedAt": "2025-01-15T10:35:00.000Z"
}Prop
Type
Se dispara cuando llega correo a tu dominio verificado — la configuración se cubre en Recepción de correos.
{
"messageId": "ses-message-id",
"from": "sender@example.com",
"fromHeader": "Jane Smith <sender@example.com>",
"to": "support@yourdomain.com",
"subject": "Re: Your question",
"timestamp": "2025-01-15T10:30:00.000Z",
"recipients": ["support@yourdomain.com"],
"hasContent": true,
"body": "<html><body>This is the email body content...</body></html>",
"spamVerdict": "PASS",
"virusVerdict": "PASS",
"spfVerdict": "PASS",
"dkimVerdict": "PASS",
"dmarcVerdict": "PASS",
"processingTimeMillis": 142
}Prop
Type
Eventos de contacto
Por defecto, contact.subscribed y contact.unsubscribed no vienen con ningún dato de evento — event es simplemente {}.
Hay una excepción: cuando un rebote o una queja causó la baja automáticamente, event lleva un reason:
{
"reason": "bounce"
}Prop
Type
Eventos de segmento
segment.<name>.entry y segment.<name>.exit llevan ambos:
{
"segmentId": "seg_abc123",
"segmentName": "VIP Users"
}Prop
Type
Eventos personalizados
Para eventos que rastreas tú mismo a través de la API, la carga útil es el objeto data que hayas suministrado en la llamada track.
Sin datos de evento
Los eventos sin ningún dato asociado producen un objeto vacío {} en el campo event.
Correlacionar webhooks con solicitudes de envío
Todo evento de correo lleva un emailId — el mismo ID de registro de correo de Bitelio que devuelve POST /v1/send. Eso te da una unión directa entre eventos de webhook y las llamadas a la API que los causaron.
Ejemplo de flujo de trabajo:
- Enviar correo vía API:
POST /v1/send
{
"to": "user@example.com",
"subject": "Welcome",
"body": "Hello!"
}
Response:
{
"success": true,
"data": {
"emails": [
{
"contact": {"id": "cnt_abc", "email": "user@example.com"},
"email": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf"
}
]
}
}-
Guarda el ID
email(ac32f08e-c6b9-45d3-9824-a73dff1e3bbf) en tu base de datos -
Cuando se disparen eventos de webhook (p. ej.,
email.open,email.bounce), empárejalos usandoevent.emailId:
{
"event": {
"emailId": "ac32f08e-c6b9-45d3-9824-a73dff1e3bbf",
"messageId": "ses-message-id",
"openedAt": "2025-01-15T11:00:00.000Z"
}
}Con esta unión en su lugar, no hay razón para emparejar por correo del contacto más marca de tiempo, ni para suscribirte a webhooks de email.sent solo para capturar el messageId del proveedor.
Casos de uso comunes
Monitorización de rebotes y quejas
Reenvía rebotes y quejas a tu aplicación con un flujo de trabajo disparado en email.bounce o email.complaint — una forma directa de mantener tu propia base de datos alineada con los estados de contacto de Bitelio.
Los pasos colocados antes del webhook te permiten afinar el comportamiento:
- Condición: Restringe el webhook a rebotes duros inspeccionando
bounceType - Retraso: Inserta una pausa corta para que los eventos relacionados puedan procesarse juntos
- Actualizar contacto: Adjunta metadatos al contacto antes de que salga el webhook
Sincronizar bajas
Un flujo de trabajo disparado en contact.unsubscribed puede alertar a tu aplicación cada vez que alguien se da de baja — el patrón estándar para mantener el estado de suscripción consistente entre sistemas.
Reenvío de eventos personalizados
Los eventos que rastreas tú mismo (digamos user.signup u order.completed) pueden retransmitirse a otros servicios mediante webhooks. En efecto, Bitelio se convierte en un enrutador de eventos: una llamada a track, entrega a tantos endpoints como quieras.
Recibir webhooks de forma segura
Algunas propiedades del paso Webhook de Bitelio importan al diseñar el endpoint que lo recibe:
-
Método:
POSTconContent-Type: application/jsonpor defecto; cada paso puede sobrescribir el método. -
Tiempo de espera: las solicitudes se cortan después de 10 segundos. Si tu procesamiento tarda más, acepta la solicitud, encola el trabajo, y responde
2xxde inmediato. -
Redirecciones: se siguen como máximo 5 redirecciones, y cada salto se vuelve a comprobar contra las reglas SSRF de abajo.
-
Se requiere URL pública: el endpoint tiene que ser alcanzable desde internet público — los destinos privados e internos (loopback, rangos RFC 1918, etc.) nunca reciben entregas.
-
Esquemas: solo
http://yhttps://. Prefiere HTTPS. -
Reintentos, pero limitados a un paso: una respuesta que no sea 2xx o un tiempo de espera lanza una excepción, y la cola reintenta el job (3 intentos en total, con espera exponencial). Ese reintento vuelve a ejecutar el mismo paso para el que se encoló el job — un paso Webhook al que se llega más adelante en la misma ejecución, sin un
DELAYoWAIT_FOR_EVENTde por medio, no tiene reintento propio; un fallo ahí termina el intento de inmediato. En cualquier caso, haz que tu manejador sea idempotente, y configura elonErrordel paso acontinue(por defecto esstop) si un fallo aquí no debería detener el recorrido del contacto. -
Verifica la autenticidad con un secreto compartido: pon un encabezado secreto en el paso webhook y valídalo del lado del servidor:
// Paso Webhook → Encabezados { "Authorization": "Bearer your-shared-secret" }Cada solicitud de ese paso lleva el secreto; rótalo como lo harías con cualquier credencial compartida. Esto es mejor que la lista blanca de IP, ya que las IP de salida están sujetas a cambios.
Añadir condiciones y retrasos
Como los webhooks son pasos de flujo de trabajo ordinarios, se combinan con todo lo demás que ofrece el sistema de flujos de trabajo:
- Un paso Condición condiciona el webhook a cualquier criterio que elijas (p. ej. solo contactos en un plan particular)
- Un paso Esperar evento retiene el webhook hasta que llegue un evento de seguimiento (p. ej. ver si un contacto que rebotó se resuscribe primero)
- Un paso Retraso inserta un margen de tiempo antes del webhook