replay_limit_reached
This delivery has been replayed 3 times in the last hour. Wait for `next_replay_allowed_at` — and fix the receiving endpoint, which is what keeps failing.
When it fires
You called POST /v1/webhooks/{id}/deliveries/{delivery_id}/replay for a delivery that has already been replayed 3 times in the last 60 minutes. The window is rolling: it opens on the first replay and resets once it elapses, so a delivery you have not replayed for an hour starts fresh with all 3 again.
This is not a lifetime cap. A delivery whose endpoint was broken last week can still be replayed today, as long as it is inside the 7-day replay window.
The budget belongs to the original delivery. A replay produces a new delivery, and naming that new delivery in a replay call draws on the same budget. Chaining replays is not a way around the limit.
force: true does not raise the limit either. It only overrides the refusal to re-send a delivery that already succeeded, and it still consumes one of the 3.
Response shape
Every Repull error follows the same envelope. The code is stable and safe to switch on.
{
"error": {
"code": "replay_limit_reached",
"message": "Delivery del_xyz789 has been replayed 3 times in the last hour.",
"fix": "Wait until next_replay_allowed_at, then replay again. If the same delivery keeps failing, fix the receiving endpoint first — replaying will not change the result.",
"docs_url": "https://repull.dev/docs/errors/replay_limit_reached",
"request_id": "req_01J5X7Y8Z9ABCDEF12345678",
"replays_made": 3,
"replay_limit": 3,
"next_replay_allowed_at": "2026-06-15T15:30:00Z"
}
}How to fix
- Read the `Retry-After` response header — the number of seconds until the window clears — or `next_replay_allowed_at` in the body for the same moment as an ISO-8601 timestamp.
- Do not retry before then, and do not fall back to replaying the delivery a previous replay produced: it shares the budget.
- Use the wait. Three failed replays in an hour means the receiver is what is broken, not the delivery. Check that your endpoint returns 2xx within the 10-second delivery timeout, and that your signature check is not rejecting valid requests.
- Read `GET /v1/webhooks/{id}/deliveries` — it records the status code and response body your endpoint returned for each failure, which is usually the fastest way to see what it is complaining about.
- Confirm the fix with `POST /v1/webhooks/{id}/test/{event_type}` before spending more replays. A test event does not count against the replay budget.
- Then replay the real delivery once, and read `replaysRemaining` on the 200 response so you know what is left.
Common gotchas
- 409, not 429. The conflict is with the state of one delivery, not with the pace of your requests. Your key is not throttled: other endpoints keep working, and replays of other deliveries go through normally. Routing this into the same global backoff you use for rate_limited will stall work that was never blocked.
Retry-Afteris in seconds. Multiplying it by 1000 twice is the usual JavaScript slip.- Automatic retries already happened. Every failed delivery is retried 5 times over roughly 26 hours before you see it in the failed list. A manual replay is for after you have fixed something, not a way to try the same broken receiver again.
- Track the budget from the success response. A 200 carries
replayNumber,replaysRemainingandnextWindowResetAt. Reading those is cheaper than discovering the limit with a 409.
Examples
curl
# The 4th replay inside the window
curl -X POST https://api.repull.dev/v1/webhooks/wh_abc123/deliveries/del_xyz789/replay \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# HTTP/1.1 409 Conflict
# Retry-After: 2280
# { "error": { "code": "replay_limit_reached", "next_replay_allowed_at": "2026-06-15T15:30:00Z", ... } }
# What did the endpoint actually return on the failures?
curl "https://api.repull.dev/v1/webhooks/wh_abc123/deliveries?status=failed" \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# Confirm the endpoint is healthy again — test events do not use replay budget
curl -X POST https://api.repull.dev/v1/webhooks/wh_abc123/test/reservation.created \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# After the window clears, replay once more
curl -X POST https://api.repull.dev/v1/webhooks/wh_abc123/deliveries/del_xyz789/replay \
-H "Authorization: Bearer sk_live_YOUR_KEY"
# HTTP/1.1 200 OK
# { "id": "del_new456", "replayNumber": 1, "replaysRemaining": 2, "nextWindowResetAt": "..." }TypeScript
const API = 'https://api.repull.dev'
const headers = {
Authorization: `Bearer ${process.env.REPULL_API_KEY}`,
'Content-Type': 'application/json',
}
async function replayDelivery(webhookId: string, deliveryId: string) {
const res = await fetch(
`${API}/v1/webhooks/${webhookId}/deliveries/${deliveryId}/replay`,
{ method: 'POST', headers },
)
if (res.status === 409) {
const { error } = await res.json()
if (error.code === 'replay_limit_reached') {
// Scoped to this delivery only — keep replaying other deliveries.
// Do not chain onto the delivery a previous replay produced: same budget.
const retryAfter = Number(res.headers.get('Retry-After') ?? 60)
throw new ReplayBlocked(deliveryId, retryAfter, error.next_replay_allowed_at)
}
}
const delivery = await res.json()
if (delivery.replaysRemaining === 0) {
console.warn(`No replays left for ${deliveryId} until ${delivery.nextWindowResetAt}`)
}
return delivery
}
class ReplayBlocked extends Error {
constructor(
readonly deliveryId: string,
readonly retryAfterSeconds: number,
readonly nextAllowedAt: string,
) {
super(`Replay budget spent for ${deliveryId}; next attempt at ${nextAllowedAt}`)
}
}If you're an AI agent
This delivery has been replayed 3 times in the last hour. The limit is per delivery, not per API key — do NOT back off globally, and do not replay the delivery a previous replay produced, because it shares the same budget. Wait until next_replay_allowed_at (or Retry-After seconds). More importantly: three failed replays means the receiving endpoint is broken, so investigate that instead of queueing a 4th attempt. Read GET /v1/webhooks/{id}/deliveries for the response the endpoint returned, fix it, verify with POST /v1/webhooks/{id}/test/{event_type} (free), then replay once.
Related
- Error reference — the full table of error codes
- Using Repull from AI agents — patterns for handling errors in agent loops
- Webhook retries & replay
- delivery_already_succeeded (409)
- rate_limited (429)
- Manage webhooks
Hit an error that isn't covered? Email hello@repull.dev with the request id from the response headers.