# Retries & idempotency

Failed deliveries retry automatically with exponential backoff. Retries use the same delivery ID, so your destination can safely deduplicate them.

| Attempt | Timing      | What happens                                   |
| ------- | ----------- | ---------------------------------------------- |
| 1       | Immediately | Initial request to the destination.            |
| 2       | +30 seconds | Retried after a timeout or `5xx` response.     |
| 3       | +2 minutes  | Backoff doubles each attempt.                  |
| 4       | +10 minutes | Still using the same delivery ID.              |
| 5       | +1 hour     | Final attempt, then marked failed and alerted. |

## Deduplicate at the destination

Store `X-Hooks-Delivery` in a unique index before processing. If the same ID arrives again, return `200` without repeating side effects. A `2xx` response stops retries immediately.

Rate limits and timeouts also trigger retries. Return a `4xx` only when retrying cannot help — for example, a malformed payload or an unknown account.

## Manual retry and replay

- `POST /v1/deliveries/{id}/retry` re-queues a failed or stuck delivery.
- `POST /v1/events/{id}/replay` replays an event to its destinations.
- `POST /v1/deliveries/{id}/cancel` cancels a queued delivery.

Only replay events when the destination state is known to be safe; a replay creates new delivery attempts against the same destinations.
