Cómo recibir webhooks
1
Configura la URL en el dashboard
Abre tu proyecto y ve a Configuración → Integraciones → Webhooks. Pega la URL pública de tu endpoint. Las URLs por proyecto y qué recibe cada una están en Routing live / test / environment.
2
Expón tu endpoint local
En desarrollo, crea un túnel a tu servidor local con ngrok (
ngrok http 3000) y usa la URL pública generada como URL del webhook.3
Valida la firma
Cada POST incluye el header
X-RQE-Signature. Verifica el HMAC sobre el body raw antes de procesar el evento. Ver más en Verificación HMAC.4
Responde 200
Devuelve un status
2xx cuanto antes. Cualquier otro status, timeout o error de red activa reintentos. Ver Retry y respuesta esperada.5
Pasa a producción
Reemplaza la URL del túnel por la de tu servidor. El flag
is_test del payload distingue el tráfico de envíos con sk_test_*.Eventos
Payload outbound
Todos los eventos outbound (email.send, email.delivery, email.bounce, email.complaint, email.reject, email.deliverydelay, email.open, email.click) comparten la misma estructura top-level. Solo cambia data.
Campos de data
Los eventos
email.send y email.delivery no incluyen un campo delivered_at separado — usa event_timestamp.email.suppressed
Se emite cuando envías vía API a una dirección que está en la lista de supresión del proyecto (rebote duro o queja previos): el correo se bloquea antes de salir y la respuesta de /v1/send-email ya lo indica con suppressed: true (ver Destinatario suprimido). Úsalo para marcar la dirección en tu sistema y dejar de intentar.
Particularidades vs los demás eventos outbound:
- Solo se emite para envíos vía API — campañas y automatizaciones no lo disparan.
- Su payload es reducido: el top-level no incluye
is_testnienvironment, y no haymessage_id— el correo nunca salió.datatraeemail,activity_id,email_type,suppressed_aty el motivo (suppression_reason,suppression_source,suppression_description).
suppression.added / suppression.removed
email.suppressed avisa cuando intentas enviar a una dirección ya suprimida. Estos dos avisan en el momento en que la dirección entra o sale de la lista, sin esperar a un envío.
Es la diferencia que importa si mantienes tu propio CRM: sin ellos, un contacto se te muere en silencio y solo te enteras en el siguiente intento, que puede ser semanas después.
suppression.added se emite cuando RQE suprime automáticamente una dirección tras un rebote duro o una queja de spam.
reason es complaint y en lugar de los campos de rebote llega complaint_feedback_type.
suppression.removed se emite cuando una dirección se quita de la lista desde el dashboard o la API — útil porque puede haberla reactivado otra persona de tu equipo, no tu sistema.
suppression.added no se emite para los rebotes de subtipo OnAccountSuppressionList: ese es el eco de la lista del proveedor, no un rebote nuevo, y no genera una supresión en tu proyecto.En una remoción masiva se emite un evento por dirección hasta un tope de 200; si lo superas queda registrado en los logs.domain.send_ready
Se emite una sola vez, cuando el dominio pasa a poder enviar. Llega antes que domain.verified: SES habilita el envío en cuanto reconoce el dominio, mientras DKIM puede seguir propagando un rato más.
Sirve para desbloquear al remitente sin tener que fingir que la autenticación ya terminó. Si automatizas altas de dominio, este es el evento que te deja habilitar el envío en tu propia UI; domain.verified llega después y confirma que además hay firma DKIM alineada.
Un dominio que puede enviar pero todavía no tiene DKIM verificado entrega peor: sin firma alineada, DMARC no pasa por DKIM. Habilita el envío si lo necesitas, pero espera a
domain.verified antes de mandar volumen.domain.verified / domain.failed
Se emiten cuando un dominio de envío cambia de estado de verificación. Son la alternativa a consultar GET /domains/{domain}/status en bucle: si automatizas el alta de dominios, escucha estos eventos en lugar de pollear.
Solo en la transición. El estado de los dominios
pending se re-consulta periódicamente, pero el webhook se emite una única vez, cuando el estado realmente cambia. Un dominio que sigue verified no vuelve a emitir. previous_verification_status te dice desde qué estado venía.domain.failed llega con la misma forma, con verification_status: "failed" y can_send: false. Revisa details para saber qué parte falló (domain_verification o dkim_verification) y llama a POST /domains/{domain}/recreate si DKIM quedó atascado.Headers de la request
RQE hace POST a tu URL con:Verificación HMAC
Routing live / test / environment
Cada proyecto tiene URLs configurables:
Sin
environment custom, cada evento outbound se entrega a todas las URLs configuradas (Producción y Desarrollo a la vez): el routing no filtra por modo del envío. El header X-RQE-Environment indica el slot de cada POST (live / dev); el flag is_test del payload dice si el envío usó sk_test_*.
Si configuras inbound_webhook_url / inbound_webhook_url_dev, esas URLs reemplazan a webhook_url / webhook_url_dev y reciben todos los eventos (outbound + inbound) — modelo “una URL recibe todo”.
Los webhooks inbound (replies) van a una sola URL según el modo del envío original (ver fallback abajo). El override por environment usa inbound_webhook_environments[<key>].
Fallback automático
Aplica solo aemail.inbound; los eventos outbound no tienen fallback porque van a todas las URLs configuradas. RQE usa la primera URL no vacía de esta cadena:
- Envío original con
sk_test_*:inbound_webhook_url_dev→webhook_url_dev→inbound_webhook_url→webhook_url - Envío original live:
inbound_webhook_url→webhook_url→inbound_webhook_url_dev→webhook_url_dev
environment declarado en el envío no está configurado, el envío entero falla con 400 ENVIRONMENT_NOT_CONFIGURED. Ver Webhook environments.
is_test en el payload
webhook_url para recibir todo en una sola URL. Distingue el tráfico test con el flag is_test.
Inbound (replies con adjuntos)
Cuando un destinatario responde tu email, RQE captura la respuesta y la dispara comoemail.inbound.
Cuándo dispara
Solo cuando el email outbound original se envió vía API (POST /v1/send-email, POST /send-email o POST /v1/send-batch). En ese caso el Reply-To lleva un token único:
Reply domain branded (opcional, sin token)
Un proyecto puede configurar un dominio de reply propio (reply.tudominio.com) para que el Reply-To salga limpio y branded, sin el token codificado:
Message-ID identifica el hilo (headers In-Reply-To / References). El evento email.inbound que recibes es idéntico: sigues leyendo activity_id y thread_id del payload, no la dirección. El token siempre fue plomería interna de RQE, no algo que tu integración parsee — así que activar un reply domain no cambia tu código.
Se habilita por proyecto publicando 2 registros DNS (un TXT de verificación SES en
_amazonses.reply.tudominio.com + un MX reply.tudominio.com → inbound-smtp.us-east-1.amazonaws.com) y coordinando con RQE. La configuración self-service está en camino. Sin reply domain configurado, se usa el token de siempre.Body del payload inbound
is_test y, si el envío original usó un environment custom, también environment.
Campos clave
Límite de tamaño
150 KB total por reply (incluyendo adjuntos en MIME base64). Replies que lo excedan rebotan con"Message length exceeds limit set by recipient". Workaround: pide al contacto que comparta archivos grandes vía link Drive/WeTransfer.
Retry y respuesta esperada
- Timeout: 10 segundos por intento.
- Intentos (eventos outbound): 5 en total — 1 inicial + 4 reintentos con backoff exponencial.
email.inbound: un solo intento, sin reintentos.- Idempotencia: los retries pueden re-entregar el mismo evento; si entregas a ambas URLs y solo una falla, el reintento re-entrega a las dos. Usa el par
(event, data.activity_id, data.event_timestamp)para deduplicar lado-cliente.
Próximos pasos
- Tracking — qué eventos disparan webhook y cómo se generan.
- Test mode — separar tráfico dev/prod vía
sk_test_*. - Webhook environments — N URLs por proyecto vía campo
environment. - Send email — cómo disparar emails que generen estos eventos.