Ir al contenido

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.

Cada intento tiene 15 segundos para terminar. Lo único que cuenta es el código HTTP:

RespuestaQué pasa
2xxEntregado. Terminó
410El endpoint se desactiva en el acto, y la entrega queda como falla, sin reintento
429, 502, 503, 504Falla. El siguiente intento respeta el Retry-After, si viene
3xxFalla. Las redirecciones nunca se siguen
Cualquier otro códigoFalla, y se reintenta según el calendario
Sin respuestaFalla (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.

Después del primer intento, los siguientes esperan:

IntentoEspera 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.

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.

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.

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:

errorQué pasó
timeoutEl intento pasó de 15 segundos
connection_refusedEl servidor rechazó la conexión
connection_resetLa conexión se cayó en el medio
host_unreachableNo fue posible llegar al servidor
dns_failedEl nombre no resolvió
tls_errorCertificado inválido, vencido o autofirmado, o una falla de TLS
redirect_not_followedEl servidor respondió 3xx
blocked_targetEl nombre pasó a apuntar a una dirección interna
blocked_protocol, blocked_port, credentials_in_url, invalid_urlLa URL ya no pasa la política de destino
network_errorOtra falla de red
endpoint_disabledEl endpoint estaba desactivado; la entrega se cerró sin envío
subscriber_access_lostQuien suscribió el endpoint ya no lee el módulo del evento; cerrada sin envío
plan_without_apiEl plan de la empresa ya no tiene la API; cerrada sin envío
internal_errorUna 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.

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-id y el id del cuerpo son los mismos del evento original. Si su sistema ya había procesado ese evento, es por el webhook-id que 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:

RespuestaCuándo
404 resource_not_foundLa 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 conflictYa 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 conflictQuien 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_missingLa 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_unavailableEl plan de la empresa no tiene la API

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.

  • 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.