Ir a la documentación
Base de conocimiento

Webhooks

Avisan a tu endpoint cuando llega correo, en lugar de obligarte a consultar en bucle.

Detalles

  • Utilizable hoy desde Configuración → Webhooks y por la API: registra un endpoint https, elige cuáles de los veinte eventos quiere y copia el secreto de firma whsec_, que se muestra al crearlo y al rotarlo, y nunca más. Las entregas son POST firmados reales que levanta el propio buzón y no una llamada a la API, así que se disparan con el correo entrante y con las aperturas y los clics, sea lo que sea lo que envió el mensaje. El envío se dispara desde todas las superficies, y antes solo lo hacía desde algunas: un envío por la API, por MCP, por una plantilla o por una regla levantaba email.sent mientras que un mensaje enviado desde el propio redactor de la aplicación no, porque el redactor escribe directamente en el buzón y no a través del servicio de envío que emitía el evento. Ahora el evento se levanta en el propio buzón, que es donde confluyen todos, así que redactar en la aplicación, programar para el martes y hacer un POST a la API son tres formas de provocar el mismo webhook. Un envío diferido lo dice dos veces: email.scheduled o email.queued cuando se acepta, email.sent cuando sale de verdad, y email.cancelled si lo retiras entremedias. Diez endpoints por buzón, aplicado allí donde se registre uno y no solo en esta pantalla.
  • Los eventos vienen en tres familias. Quince son sobre un mensaje: email.received, email.replied, email.sent, email.delivered, email.failed, email.cancelled, email.scheduled, email.queued (el hermano de scheduled para deshacer el envío), email.delivery_delayed, email.bounced, email.complained, email.suppressed, email.opened, email.clicked y email.downloaded. email.sent significa que el servicio de envío aceptó el mensaje, email.delivered que lo hizo el servidor receptor, y email.delivery_delayed que todavía no ha llegado y se sigue reintentando. email.replied se dispara junto a email.received cuando el mensaje que llega responde a otro que ya está en el buzón, así que un consumidor que quiera ambos recibe ambos. email.downloaded se dispara cuando una persona descarga un archivo que salió como enlace de descarga, con el mismo clasificador que deja fuera del recuento a escáneres y generadores de vistas previas de enlaces, y no nombra a ningún destinatario, porque el enlace es el mismo para todos aquellos a los que fue el mensaje. Tres son sobre un dominio: domain.verified cuando empieza a recibir, domain.sending_changed cuando cambia su veredicto de envío, y domain.deleted cuando se elimina, ya sea porque lo pediste o porque el proceso de limpieza de los siete días lo descartó sin verificar. Dos son sobre la propia lista de supresión, que es algo distinto de email.suppressed: suppression.added cuando se añade una dirección, suppression.removed cuando se vuelve a permitir una. No suscribirse a ninguno significa todos los eventos de mensaje salvo email.replied, catorce hoy, y nunca una familia añadida después, y la API lo devuelve como ["*"]. Nombra los eventos que quieras si prefieres ser explícito. Cada entrega lleva X-OpenEmail-Signature con la forma t=<unix>,v1=<hex>, un HMAC-SHA-256 sobre la marca de tiempo, un punto y el cuerpo en bruto, además de X-OpenEmail-Event y X-OpenEmail-Delivery. Verifica contra los bytes tal y como llegaron: analizarlos y volver a serializarlos reordena las claves y rompe la firma. La ventana de repetición de 300 segundos la debe aplicar el receptor, y el verificador del SDK la usa por defecto.
  • Se rechaza el registro de cualquier cosa que no sea https o que no sea enrutable públicamente (loopback, RFC1918, link-local, CGNAT y sus equivalentes en IPv6), y no se siguen las redirecciones, así que un 3xx se registra como entrega fallida en lugar de perseguirse a otro sitio. El receptor dispone de 5 segundos, los endpoints se entregan en paralelo, de modo que diez de ellos siguen costando 5 segundos y no 50, y los intentos recientes se listan en la página de ese endpoint con el código de respuesta y lo que tardó.
  • Una entrega se intenta hasta cinco veces. La primera sale en el momento en que ocurre el evento; un fallo que podría resolverse por sí solo se reintenta al cabo de 1 minuto, luego 5, luego 25 y luego 2 horas, lo que reparte un evento a lo largo de unas dos horas y media. Los reintentos se guardan como trabajo duradero y no en memoria, así que un despliegue en mitad de esa ventana no los pierde. Solo se repiten los fallos que merece la pena repetir: un tiempo de espera agotado, una conexión rechazada, 408, 425, 429 o cualquier 5xx. Cualquier otro 4xx es el endpoint rechazando el payload a propósito, y preguntar cuatro veces más sería cuatro veces la carga para la misma respuesta. El id del evento se acuña una sola vez y todos los intentos lo llevan en X-OpenEmail-Delivery, así que un receptor que vea el mismo id dos veces puede descartar el segundo en lugar de actuar dos veces. Después de que 100 eventos seguidos fallen en todos sus intentos, el endpoint se desactiva, se envía un correo al espacio de trabajo y el motivo se puede leer en el propio endpoint. Un endpoint que responde 410 Gone se desactiva en el acto.
  • Un endpoint que falla 100 veces seguidas se apaga en lugar de seguir siendo llamado para siempre, y se avisa por correo a todos los que tienen acceso a los webhooks: cuál es, qué informó el último intento y que no se encoló nada mientras fallaba. El recuento es CONSECUTIVO y cualquier intento entregado lo reinicia, así que una mala tarde del marzo pasado no puede sumar hasta desactivar un endpoint hoy. Volver a activarlo también pone el recuento a cero. La consola distingue los dos estados en lugar de mostrar un único conmutador: un endpoint que apagaste tú se ve distinto de uno que apagamos nosotros.
  • Gestionar endpoints es un solo trabajo con dos puertas de entrada. Por la API son POST /webhooks, el patch, el delete, la rotación del secreto, la prueba y el registro de entregas, con un método para cada uno en el SDK; en la aplicación es Configuración → Webhooks, contra el mismo registro y no contra un segundo. La lectura está sujeta a webhooks:read, así que cualquiera que esté construyendo una integración puede ver los endpoints y su historial de entregas (cuál se disparó, qué respondió el receptor, cuánto tardó) sin ser el propietario. Registrar, editar, probar, rotar y eliminar requieren webhooks:write Y la propiedad del buzón, en ambas superficies, y esa segunda mitad es deliberada: un endpoint no tiene eje de direcciones, así que recibe todas las direcciones que tiene el espacio de trabajo, con sus asuntos y destinatarios, y ningún permiso significa “se le puede enviar todo eso”. Un rol que construye integraciones y no lee el correo lo maneja con una clave de espacio de trabajo.