Delivery & retries

What happens after a publish is accepted: the retry schedule, delivery statuses, the dead-letter queue, replay, and per-attempt logs.

After Repost accepts a message, each subscribed endpoint gets its own delivery, with its own attempt history, schedule, and status. One customer's dead endpoint never affects another's, and a message to three endpoints is three independent deliveries.

What counts as delivered

A delivery attempt is an HTTPS POST of the signed envelope to the endpoint URL. A 2xx response completes the delivery. Anything else is a failure: 4xx, 5xx, timeouts, connection errors, and also 3xx, because redirects are never followed. Following one would replay a signature to a location the customer didn't register.

The retry schedule

A failing delivery is tried 11 times over roughly 24 hours: the first attempt plus ten retries, with jitter applied so retries don't arrive in herds at a recovering endpoint.

RetryDelay after previous attempt
15 seconds
230 seconds
32 minutes
410 minutes
530 minutes
61 hour
72 hours
84 hours
98 hours
108 hours

If the endpoint responds with a Retry-After header longer than the scheduled delay, Repost honours it, up to a cap of one hour. When the last retry fails, the delivery moves to the dead-letter queue with its full attempt history.

Endpoint state changes cut the schedule short instead of wasting attempts. Pausing an endpoint holds its deliveries until resume, which then drains the backlog in order. Disabling one fails its deliveries terminally as endpoint_disabled, and deleting one fails them as endpoint_gone. Updating an endpoint's URL or headers applies to pending retries too.

Delivery statuses

StatusMeaning
pendingCreated, first attempt not yet made, or held by a pause.
retryingAt least one attempt failed and more are scheduled.
successAn attempt received a 2xx.
failedEnded without exhausting retries (endpoint_gone, endpoint_disabled).
dlqRetries exhausted; parked in the dead-letter queue.

Deliveries can be listed and inspected over the API, in the dashboard's Messages view, or by the customer in their portal:

curl "https://api.repost.sh/v1/deliveries?status=dlq&limit=50" \
  -H "Authorization: Bearer $REPOST_TOKEN"
 
curl "https://api.repost.sh/v1/messages/msg_01JZX.../deliveries" \
  -H "Authorization: Bearer $REPOST_TOKEN"

Attempt logs

Every attempt is recorded: the response status, latency, an error class such as TIMEOUT or CONNECTION_FAILED, and a bounded excerpt of the response body and headers (up to 4 KiB each). Sensitive request headers are stored as [redacted]; only content-type, user-agent, webhook-id, and webhook-timestamp are kept verbatim, and Set-Cookie response headers are dropped. There is enough to debug a failing endpoint without secrets ending up in logs.

Customers see the same attempt history for their own endpoints, live, in the portal.

Replay and redeliver

A delivery in the dead-letter queue is not lost. Replay it with one call, or re-send an already-delivered message to a specific endpoint, for example after a customer restores from a backup:

# Replay a delivery
curl -X POST https://api.repost.sh/v1/deliveries/{deliveryId}/replay \
  -H "Authorization: Bearer $REPOST_TOKEN"
 
# Redeliver a message to one endpoint
curl -X POST https://api.repost.sh/v1/messages/{messageId}/redeliver \
  -H "Authorization: Bearer $REPOST_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"endpointId": "..."}'

Both return 202 and re-enter the normal pipeline, signed with the endpoint's current secrets and recorded like any other attempt. Customers can replay their own dead-lettered deliveries from the portal.

Receivers should treat delivery as at-least-once and dedupe on the webhook-id header. Retries after a lost 2xx, replays, and redeliveries can all deliver the same message more than once, and webhook-id is stable across all of them.

Continue