> ## Documentation Index
> Fetch the complete documentation index at: https://repost.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Delivery, Retries, and Recovery

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

| Retry | Delay after previous attempt |
| ----- | ---------------------------- |
| 1     | 5 seconds                    |
| 2     | 30 seconds                   |
| 3     | 2 minutes                    |
| 4     | 10 minutes                   |
| 5     | 30 minutes                   |
| 6     | 1 hour                       |
| 7     | 2 hours                      |
| 8     | 4 hours                      |
| 9     | 8 hours                      |
| 10    | 8 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

| Status     | Meaning                                                                  |
| ---------- | ------------------------------------------------------------------------ |
| `pending`  | Created, first attempt not yet made, or held by a pause.                 |
| `retrying` | At least one attempt failed and more are scheduled.                      |
| `success`  | An attempt received a `2xx`.                                             |
| `failed`   | Ended without exhausting retries (`endpoint_gone`, `endpoint_disabled`). |
| `dlq`      | Retries 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:

```bash theme={"languages":{"custom":["/languages/repost.json"]}}
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](/docs/send/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:

```bash theme={"languages":{"custom":["/languages/repost.json"]}}
# 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.

<Tip>
  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.
</Tip>

## Continue

<Columns cols={3} className="gap-y-4">
  <Card title="Signing" icon="lock" href="/docs/send/signing" cta="Verify" arrow="true">
    The headers on every delivery, and how customers verify them.
  </Card>

  <Card title="Customer portal" icon="app-window" href="/docs/send/portal" cta="Self-service" arrow="true">
    Delivery logs, the dead-letter queue, and replay, in your customer's hands.
  </Card>

  <Card title="HTTP API" icon="globe" href="/docs/send/publishing" cta="Publish" arrow="true">
    The idempotency rules that feed this pipeline exactly once.
  </Card>
</Columns>
