Entregas y reintentos
Cada evento se convierte en una entrega para cada endpoint activo y suscrito a él. La entrega tiene su propia vida: intentos, estado y resultado, que usted sigue en el registro.
Qué significa su respuesta
Sección titulada «Qué significa su respuesta»Cada intento tiene 15 segundos para terminar. Lo único que cuenta es el código HTTP:
| Respuesta | Qué pasa |
|---|---|
2xx | Entregado. Terminó |
410 | El endpoint se desactiva en el acto, y la entrega queda como falla, sin reintento |
429, 502, 503, 504 | Falla. El siguiente intento respeta el Retry-After, si viene |
3xx | Falla. Las redirecciones nunca se siguen |
| Cualquier otro código | Falla, y se reintenta según el calendario |
| Sin respuesta | Falla (tiempo agotado, conexión rechazada, error de TLS, DNS), y se reintenta |
El cuerpo de la respuesta no necesita nada: un 200 vacío basta. Los encabezados de la respuesta no se leen, salvo el Retry-After, y no se guardan. Del cuerpo, hasta 2 KB quedan guardados en el registro de entregas, así que no responda con datos sensibles.
Use el 410 solo cuando la dirección dejó de existir para siempre: desactiva el endpoint para todos los eventos, no solo para ese.
El calendario de reintentos
Sección titulada «El calendario de reintentos»Después del primer intento, los siguientes esperan:
| Intento | Espera después del anterior |
|---|---|
| 2.º | 5 segundos |
| 3.º | 5 minutos |
| 4.º | 30 minutos |
| 5.º | 2 horas |
| 6.º | 5 horas |
| 7.º | 10 horas |
| 8.º | 14 horas |
| 9.º | 20 horas |
| 10.º | 24 horas |
Cada espera varía un 10% hacia arriba o hacia abajo, al azar, para que muchas entregas que fallaron juntas no vuelvan todas en el mismo segundo. Son 10 intentos en poco más de 3 días. Si el décimo falla, la entrega queda como failed, y usted todavía puede reenviarla mientras el evento exista.
Retry-After
Sección titulada «Retry-After»En las respuestas 429, 502, 503 y 504, el Retry-After se respeta, en segundos o como fecha HTTP. Solo alarga la espera: el siguiente intento sale en lo que sea más tarde entre el calendario y el Retry-After. El tope es de 24 horas, así que un Retry-After mayor vale 24 horas. Un valor inválido se ignora, y no agrega intentos: después del décimo, no hay otro.
Desactivación automática
Sección titulada «Desactivación automática»El endpoint que solo falló durante 3 días seguidos se desactiva. La cuenta empieza en la primera falla y solo una entrega con éxito la pone en cero: un endpoint que a veces falla y a veces acierta nunca se desactiva por eso.
En los dos casos automáticos (3 días de falla y 410), el dueño y los administradores reciben un correo con el dominio del endpoint y la fecha. El endpoint aparece con status: "disabled" y disabled_reason failing o gone.
disabled_reason tiene cuatro valores: manual (alguien lo desactivó, en el panel o con PATCH), failing (3 días de falla), gone (el destino respondió 410) y emergency_key_rotation (pausado por seguridad: la clave de API que suscribió el endpoint pasó por la rotación “Detener ahora” o, cuando la inscripción en el programa de GitHub esté activa, fue revocada automáticamente por haber sido encontrada publicada). Con el endpoint activo, el campo es null.
Mientras el endpoint está desactivado:
- ningún evento nuevo genera entrega para él, y esos eventos no quedan guardados esperándolo;
- la entrega que ya estaba en la cola se cierra sin envío, con el error
endpoint_disabled.
Para volver a activarlo, PATCH /public/v1/webhook_endpoints/{id} con "status": "enabled". Activarlo de nuevo pone en cero la cuenta de fallas. Los eventos del período en que estuvo desactivado no llegan a ese endpoint y no se pueden reenviar a él, porque no se creó ninguna entrega. Para recuperar lo que cambió en ese período, sincronice con updated_after (vea Buenas prácticas). El reenvío manual vale para las entregas que fallaron antes de la desactivación.
El registro de entregas
Sección titulada «El registro de entregas»GET /public/v1/webhook_endpoints/{id}/deliveries lista las entregas del endpoint, de las más nuevas a las más antiguas, con filtro por status (pending, succeeded, failed) y por event_type. Cada entrega trae el estado, la cantidad de intentos, el próximo intento, el código HTTP y la duración del último, hasta 2 KB del cuerpo de la respuesta y, cuando no hubo respuesta, el motivo en error.
La lista sigue la misma regla que GET /public/v1/events: solo aparecen las entregas de eventos de los módulos que la clave lee (<modulo>:read) y que su miembro puede ver en el panel.
Los valores de error:
error | Qué pasó |
|---|---|
timeout | El intento pasó de 15 segundos |
connection_refused | El servidor rechazó la conexión |
connection_reset | La conexión se cayó en el medio |
host_unreachable | No fue posible llegar al servidor |
dns_failed | El nombre no resolvió |
tls_error | Certificado inválido, vencido o autofirmado, o una falla de TLS |
redirect_not_followed | El servidor respondió 3xx |
blocked_target | El nombre pasó a apuntar a una dirección interna |
blocked_protocol, blocked_port, credentials_in_url, invalid_url | La URL ya no pasa la política de destino |
network_error | Otra falla de red |
endpoint_disabled | El endpoint estaba desactivado; la entrega se cerró sin envío |
subscriber_access_lost | Quien suscribió el endpoint ya no lee el módulo del evento; cerrada sin envío |
plan_without_api | El plan de la empresa ya no tiene la API; cerrada sin envío |
internal_error | Una falla de nuestro lado. Sigue el calendario de reintentos como cualquier falla |
Cuando hubo respuesta HTTP, error llega en null y el motivo está en response_status_code (el 3xx es la excepción: llega con redirect_not_followed). endpoint_disabled, subscriber_access_lost y plan_without_api no gastan intento: la entrega se cierra sin enviarse.
Reenvío manual
Sección titulada «Reenvío manual»POST /public/v1/webhook_endpoints/{id}/deliveries/{delivery_id}/retry responde 202 y crea una entrega nueva del mismo evento para el mismo endpoint. La entrega anterior queda como estaba, con su historial, y la nueva tiene un retry_number mayor.
El reenvío es un pedido explícito suyo: vuelve a verificar el acceso de quien suscribió el endpoint, pero no verifica si el endpoint sigue suscrito a ese tipo de evento. Una entrega de sale.paid se puede reenviar aunque el endpoint ya no esté suscrito a sale.paid.
- La entrega nueva sigue el mismo camino que cualquier otra: firmada con el secreto actual, con la misma verificación de destino y el mismo calendario de reintentos si falla.
- El
webhook-idy eliddel cuerpo son los mismos del evento original. Si su sistema ya había procesado ese evento, es por elwebhook-idque reconoce la repetición. - El cuerpo es el del evento, tal como se registró. El objeto no se vuelve a leer: para el estado actual, consulte la API.
Los rechazos:
| Respuesta | Cuándo |
|---|---|
404 resource_not_found | La entrega no existe, es de otro endpoint, o el evento tiene más de 30 días |
409 conflict con param: "status" | El endpoint está desactivado. Actívelo antes |
409 conflict | Ya hay una entrega de ese evento para ese endpoint en la cola (la original todavía reintentando, u otro reenvío). Espere a que termine |
409 conflict | Quien suscribió el endpoint ya no lee el módulo del evento. Devuelva el acceso o edite el endpoint con alguien que lo tenga |
403 permission_missing | La clave no tiene el <modulo>:read de algún evento al que el endpoint está suscrito (la misma regla del cambio de URL) |
403 plan_feature_unavailable | El plan de la empresa no tiene la API |
Por cuánto tiempo se guarda
Sección titulada «Por cuánto tiempo se guarda»Los eventos y las entregas se guardan durante 30 días y después se eliminan. Ese es el plazo del registro de entregas, de GET /public/v1/events y del reenvío manual.
Próximo paso
Sección titulada «Próximo paso»- Buenas prácticas: cómo armar la recepción para aguantar repeticiones y desorden.
- Referencia: las rutas de entregas y de eventos, campo por campo.