Guía de webhooks
Cómo recibir y verificar los eventos que Mailronix envía a tu dirección: entrega, firma, contenido, reintentos y rotación del secreto.
Cómo llegan las entregas
Cada evento es una petición POST con cuerpo JSON a la dirección HTTPS que registraste (puerto 443 u 8443). Responde en menos de 10 segundos: cualquier código 2xx confirma la entrega. Cualquier otra cosa es un error y provoca un nuevo intento, incluidas las redirecciones, que no se siguen. Las direcciones no accesibles desde Internet público se rechazan.
- Mailronix-Signature
- La firma de la petición. Consulta «Verificar la firma».
- Mailronix-Event-Id
- El identificador del evento, igual al campo id del cuerpo. Es el mismo en cada intento.
- Mailronix-Delivery-Id
- El identificador de la entrega. Cítalo cuando escribas a soporte.
- Mailronix-Event-Type
- El tipo de evento, igual al campo type del cuerpo.
Verificar la firma
Verifica siempre la firma antes de fiarte del contenido: cualquiera que conozca tu dirección puede enviarle una petición. La firma es el HMAC-SHA256, en hexadecimal, de la cadena «t.cuerpo», con el cuerpo exactamente como lo recibiste.
Mailronix-Signature: t=<unix>,v1=<hmac-sha256>
v1 = hex( HMAC-SHA256( chiave, "<t>." + corpo ) )- Clave: el secreto que la consola muestra una sola vez tiene la forma «whsec_…». La clave HMAC son los bytes que siguen al prefijo, decodificados desde base64url sin relleno. El texto del secreto no es la clave.
- Cuerpo: calcula el HMAC sobre los bytes en bruto recibidos. Si tu framework analiza el JSON antes que tú, recupera el cuerpo en bruto (en Express, express.raw): reescribir el JSON cambia los bytes y la firma no coincide.
- Frescura: rechaza las peticiones en las que t se aleje más de cinco minutos de la hora actual. t es el instante de cada intento, así que un reintento horas después también supera la comprobación.
- Varias firmas: en las 24 horas posteriores a una rotación la cabecera contiene dos valores v1, uno para el secreto nuevo y otro para el anterior. Acepta la petición si cualquiera de ellos coincide con alguno de los secretos que conoces.
- Comparación: usa una comparación en tiempo constante, como en los ejemplos.
Ejemplos de verificación
import { createHmac, timingSafeEqual } from 'node:crypto'
// `segreto` è quello mostrato dalla console, «whsec_…». La chiave HMAC sono i
// suoi byte: si toglie il prefisso e si decodifica da base64url.
const chiave = (segreto) => Buffer.from(segreto.replace(/^whsec_/, ''), 'base64url')
// `corpo` è il corpo ESATTO ricevuto (Buffer), non il JSON riscritto.
export function verificaFirma(intestazione, corpo, segreti, toleranza = 300) {
const parti = intestazione.split(',').map((p) => p.trim().split('='))
const t = parti.find(([k]) => k === 't')?.[1]
const firme = parti.filter(([k]) => k === 'v1').map(([, v]) => v)
if (!t || firme.length === 0) return false
if (Math.abs(Date.now() / 1000 - Number(t)) > toleranza) return false
let valida = false
for (const segreto of segreti) {
const atteso = createHmac('sha256', chiave(segreto)).update(`${t}.`).update(corpo).digest()
for (const firma of firme) {
const ricevuta = Buffer.from(firma, 'hex')
if (ricevuta.length === atteso.length && timingSafeEqual(ricevuta, atteso)) valida = true
}
}
return valida
}
Ejemplos ejecutados contra la firma real de Mailronix. Si la verificación falla, responde 401; tras aceptar el evento, responde 200 enseguida y trabaja después.
Eventos y contenido
Todos los cuerpos tienen el mismo sobre; el contenido de data cambia según el tipo.
{
"id": "evt_01j9x4k2m7q8r5t3v6w1y0z2ab",
"type": "email.bounced",
"api_version": "2026-10-22",
"created_at": "2026-10-22T14:03:11Z",
"brand_id": "6f1c7d0e-5a3b-4e8f-9c21-0b7d4a1e3f52",
"data": {
"email_log_id": "b2d4f6a8-1c3e-4a5b-8d7f-9e0a1b2c3d4e",
"recipient": "[email protected]",
"category": "transactional",
"bounce_type": "permanent"
}
}- id: el identificador del evento. Úsalo para reconocer duplicados.
- type: uno de los tipos de la lista de abajo, o webhook.test para la entrega de prueba.
- api_version: la versión del formato.
- created_at: cuándo ocurrió el evento, en UTC.
- brand_id: la marca a la que pertenece el evento (null solo en webhook.test).
- Los eventos email.* llevan en data email_log_id, recipient y category. email.bounced añade bounce_type (permanent, transient o undetermined): un rebote anunciado como transient y reclasificado después como permanent produce un segundo email.bounced.
- Los eventos contact.* llevan en data contact_id y email. contact_id puede ser null en contact.unsubscribed, por ejemplo cuando la baja llega desde una revocación: contempla ese caso.
Tipos de evento
- email.sent
- webhookGuide.eventTypes.email.sent
- email.delivered
- webhookGuide.eventTypes.email.delivered
- email.bounced
- webhookGuide.eventTypes.email.bounced
- email.complained
- webhookGuide.eventTypes.email.complained
- email.failed
- webhookGuide.eventTypes.email.failed
- email.suppressed
- webhookGuide.eventTypes.email.suppressed
- contact.confirmed
- webhookGuide.eventTypes.contact.confirmed
- contact.unsubscribed
- webhookGuide.eventTypes.contact.unsubscribed
No hay eventos de apertura: Mailronix no los registra, por decisión de privacidad. Los eventos email.clicked solo llegan de las marcas que activaron el seguimiento de clics y solo para los destinatarios que aceptaron; describen un clic detectado (escáneres y vistas previas pueden generarlos) y llevan el host del destino, no la URL. La revocación bloquea nuevas detecciones y no convierte retroactivamente en ilícito el evento ya recogido: los eventos anteriores ya en cola pueden llegar después, dentro de los 30 días de conservación de la cola. La eliminación del contacto elimina los eventos y las entregas en cola conforme al procedimiento previsto. El cliente receptor del webhook sigue siendo responsable de sus propias copias y de la gestión de los derechos de los interesados respecto a ellas.
Reintentos, duplicados y orden
- Una entrega fallida se reintenta hasta diez veces en casi cuatro días: de inmediato y luego a los 10 segundos, 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas, 12 horas, 24 horas y 48 horas.
- Cada evento llega al menos una vez, no exactamente una: puede llegar dos veces, por ejemplo si respondes después del límite de 10 segundos. Guarda los id ya procesados e ignora los duplicados.
- El orden de llegada no está garantizado: un email.delivered puede llegar después de un email.bounced. Para ordenar los eventos usa created_at, no la hora en que los recibes.
- Si un evento agota los intentos y en los últimos tres días no ha tenido éxito ninguna entrega a ese endpoint, el endpoint se desactiva y el propietario recibe un correo. Desde la página de Webhooks puedes reactivarlo (se pide la contraseña) y reenviar eventos concretos. Las entregas se conservan 30 días.
Rotar el secreto
Desde la página de Webhooks puedes rotar el secreto de un endpoint (se pide la contraseña); el secreto nuevo se muestra una sola vez. Durante 24 horas cada entrega lleva dos firmas, la del secreto nuevo y la del anterior: acepta ambos secretos en ese tiempo y actualiza tu receptor sin perder entregas. Si el secreto antiguo se ha filtrado, elige revocarlo de inmediato: desde ese momento solo cuenta el nuevo.
Probar el receptor
Al crear un endpoint y con la acción «Enviar prueba», Mailronix envía un evento webhook.test con el mismo sobre y la misma firma que los eventos reales, sin datos de destinatarios. Si la prueba falla, el endpoint se guarda pero queda desactivado.
Límites
Los endpoints activos por cuenta son 1 con el plan Free, 5 con Starter y 10 con Growth. Los endpoints se configuran solo desde la consola, con la sesión y nunca con una clave de API.