Qué pasa entre que una sesión se resuelve y el evento aterriza en tu endpoint — y qué pasa cuando no aterriza.
Qué cuenta como entrega exitosa
Tu endpoint tiene 10 segundos para responder con un status 2xx. Todo lo
demás es fallo y dispara el reintento:
- cualquier status fuera del rango
2xx; - un
3xx: no seguimos redirecciones (unLocationpodría apuntar a una red interna, así que un redirect se trata como error); - timeout de conexión, de headers o de cuerpo;
- una URL que en el momento del envío resuelve a una IP no permitida.
El cuerpo de tu respuesta se descarta sin leerlo: no hace falta que devuelvas
nada. Un 200 con cuerpo vacío es la respuesta ideal.
Reintentos
Cada mensaje se reintenta con backoff exponencial, hasta 8 intentos:
| Campo | Tipo | Espera antes del reintento | Descripción |
|---|---|---|---|
Intento 1 | primer envío | 10 s | Si falla, el reintento sale 10 segundos después. |
Intento 2 | reintento | 20 s | |
Intento 3 | reintento | 40 s | |
Intento 4 | reintento | 80 s | |
Intento 5 | reintento | 160 s | |
Intento 6 | reintento | 320 s | |
Intento 7 | reintento | 640 s | Último intento. |
Intento 8 | abandono | — | El mensaje se abandona: la última entrega queda registrada como dead. |
La ventana total es de aproximadamente 20 minutos. Si tu endpoint estuvo
caído más que eso, el evento no se va a re-entregar solo: usá
POST /v1/webhook-deliveries/{id}/redeliver, o reconciliá con
GET /v1/verification-sessions.
Entrega at-least-once
Podés recibir el mismo evento más de una vez. Sucede, entre otros casos:
- cuando un reintento vuelve a enviar a todos los endpoints suscriptos,
incluidos los que ya habían respondido
200; - cuando el origen re-emite el evento;
- cuando disparás una re-entrega manual.
Por eso el id de evento es determinístico: se deriva de la cuenta, la sesión
y el tipo de evento, así que el mismo evento lógico lleva siempre el mismo
evt_…, en todos los reintentos y en todos tus endpoints.
Circuit breaker por endpoint
Si un endpoint acumula 5 fallos consecutivos, se abre su circuito y queda cortado 15 minutos:
- durante ese lapso no se intenta el POST;
- el intento igual queda registrado como
failedconerror: "circuit_breaker_open"; - el mensaje se re-encola con el mismo backoff, así que si el circuito sigue abierto cuando se agotan los reintentos, el evento se abandona;
- el circuito se cierra solo al vencer el plazo, y la primera entrega exitosa resetea el contador.
Cambiar la URL del endpoint también resetea el circuito: el destino nuevo no hereda el historial de fallos del anterior.
Las entregas de prueba (/test) ignoran el circuito a propósito —son una
herramienta de diagnóstico— y tampoco lo alimentan.
Orden
Las entregas se procesan en orden por cuenta. La contracara: cuando un mensaje falla, los siguientes de tu cuenta esperan a que se resuelva o se agote. Un endpoint caído puede retrasar los eventos de otras sesiones tuyas hasta unos 20 minutos.
Es un intercambio deliberado: preferimos no entregarte un approved antes que un
rejected que ocurrió primero. De todos modos, no supongas orden en tu handler:
usá created_at del sobre y completed_at de la sesión.
Entregas administrativas
Las que disparás vos —/test y /redeliver— son de un solo tiro: no se
reintentan automáticamente. Su resultado igual queda en el log de entregas, con
el motivo si falló, así que un 202 queued: true nunca termina en la nada.
Si una prueba falla, arreglás y disparás otra. Cada llamada a /redeliver es un
intento nuevo y real: no se descarta por deduplicación.
Auditar qué pasó
GET /v1/webhook-deliveries?endpoint=whep_… devuelve el historial con el status
de cada intento, el código HTTP que respondió tu endpoint, cuánto tardó y el
motivo del fallo. Es el primer lugar donde mirar cuando "no me llegan los
webhooks". Ver Webhook endpoints.
Lista de verificación del receptor
| Campo | Tipo | Por qué | Descripción |
|---|---|---|---|
Validar la firma | Obligatorio | sin esto cualquiera puede fabricar una aprobación | Cómo hacerlo. |
Responder 2xx rápido | Obligatorio | el timeout es de 10 s | Validá, encolá y respondé. No proceses de forma sincrónica. |
Deduplicar por evt_ | Obligatorio | la entrega es at-least-once | Guardá los ids procesados. |
Ser idempotente por sesión | Recomendado | approved y completed llegan juntos | Si te suscribís a los dos, el mismo desenlace llega dos veces con eventos distintos. |
Aceptar type: "test" | Recomendado | las pruebas usan un tipo propio | Contemplalo o descartalo explícitamente en tu ruteo. |
Monitorear el log de entregas | Recomendado | detecta el circuito abierto antes que tus usuarios | Especialmente después de un deploy del receptor. |