Reintentos, orden y versionado
Cómo entregamos, qué garantizamos y cómo escribir un consumidor a prueba de fallas.
Reintentos
Si tu endpoint no responde 2xx en 10 segundos, reintentamos con espera exponencial durante 24 horas: 30 segundos, 2 minutos, 10 minutos, 1 hora, 6 horas y 24 horas.
Después de 20 entregas fallidas seguidas, o 5 días de fallas continuas, pausamos el webhook y te avisamos por email y con el evento webhook.disabled. Reactivarlo desde Configuración, Desarrolladores no pierde el historial.
Códigos de respuesta
| Campo | Tipo | Descripción |
|---|---|---|
2xx | éxito | La entrega se marca como completada. |
410 | baja definitiva | Damos de baja el webhook sin reintentar. |
429 | reintento | Respetamos el header Retry-After si viene. |
otros 4xx y 5xx | reintento | Entra en la cola de reintentos. |
Idempotencia
Un mismo evento puede llegar más de una vez: garantizamos entrega al menos una vez, no exactamente una vez.
Guardá el id del evento y descartá los repetidos. Es la forma más simple de volverte inmune a los reintentos.
async function handle(event) { // Insert que falla si el id ya existe: el evento repetido se descarta solo. const inserted = await db.processedEvents.insertIfAbsent(event.id); if (!inserted) return; await process(event);}Orden de entrega
No garantizamos el orden. Un conversation.closed puede llegar antes que un message.created de la misma conversación.
Usá occurred_at para ordenar y, si tu lógica depende del estado final, leé el objeto por API antes de escribir.
Nunca reconstruyas el estado sumando eventos en orden de llegada. Reconciliá contra la API cuando el orden importa.
Versionado
Cada entrega incluye api_version. La actual es 2026-09-01.
Agregar un evento nuevo o un campo nuevo dentro de data no se considera un cambio que rompa. Tu consumidor debe ignorar lo que no conoce.
Los cambios que rompen se publican como una versión nueva, con seis meses de convivencia y aviso previo.
Direcciones de salida
Si tu firewall filtra por IP, permití estas direcciones. Avisamos con 30 días de anticipación antes de cambiarlas.
52.14.108.0/2435.171.44.0/2418.229.201.0/24Historial de entregas
Guardamos cada intento durante 30 días, con el cuerpo enviado, la respuesta de tu servidor, el código, la duración y el número de intento. Está en Configuración, Desarrolladores, Webhooks, y también por API.
Sirve para dos cosas: entender por qué falló algo sin pedirnos logs, y recuperar eventos que tu sistema perdió durante una caída sin tener que reprocesar toda la base.
/v1/webhooks/{id}webhooks:readDevuelve el endpoint con su tasa de éxito, latencia p95 y fallos consecutivos.
/v1/webhooks/{id}/deliverieswebhooks:readLista los intentos de entrega, del más nuevo al más viejo.
| Campo | Tipo | Descripción |
|---|---|---|
status | string | success, failed o pending. |
event | string | Filtra por nombre de evento. |
limit | number | Máximo 100. |
/v1/webhooks/{id}webhooks:writeCambia la URL, los eventos o pausa el endpoint.
/v1/webhooks/{id}webhooks:writeDa de baja el endpoint y su historial.
curl "https://api.tinkay.app/v1/webhooks/wh_1/deliveries?status=failed&limit=50" -H "Authorization: Bearer dk_live_xxx"Buenas prácticas
- Respondé 200 antes de procesar. Encolá y trabajá asincrónicamente.
- Verificá la firma en todas las entregas, también en producción.
- Guardá el cuerpo crudo unos días: sirve para reprocesar sin pedir reenvíos.
- Monitoreá el registro de entregas en Configuración, Desarrolladores: ahí ves código, duración e intento de cada una.
- Usá
Reenviarpara reprocesar una entrega puntual después de arreglar un bug.
