Retries & deliveries
Puck delivers webhooks with an at-least-once guarantee. If your endpoint does not respond with a 2xx status, the delivery worker retries automatically on an exponential backoff schedule before eventually moving the delivery to the dead-letter state.
Retry schedule
Puck makes up to 5 delivery attempts total (1 initial + 4 retries). The wait between attempts grows as follows:
| Attempt | Delay before this attempt |
|---|---|
| 1 (initial) | Immediate |
| 2 | 30 seconds |
| 3 | 2 minutes |
| 4 | 10 minutes |
| 5 | 1 hour |
After the 5th failed attempt the delivery is marked dead_lettered (status failed_permanent) and no further automatic attempts are made.
The total window from first attempt to dead-letter is approximately 1 hour and 12 minutes under worst-case conditions. Plan your endpoint availability accordingly — a maintenance window longer than ~70 minutes may result in dead-lettered deliveries that need manual replay.
HTTP semantics
The delivery worker interprets HTTP response codes as follows:
| Response | Treatment |
|---|---|
2xx | Success — delivery is marked delivered; no further attempts. |
4xx (except 408 and 429) | Permanent failure — a 4xx (other than timeout/throttle) signals a configuration error on the subscriber’s side (wrong URL, auth rejected, etc.). The delivery is immediately marked failed_permanent without consuming remaining retry budget. |
408 (timeout) or 429 (rate limited) | Transient — retried with backoff. |
5xx | Transient — retried with backoff. |
| Connection error / timeout | Transient — retried with backoff. |
This behavior follows the Stripe convention: a 4xx means “you sent something wrong,” which retrying won’t fix. If you return 4xx accidentally (e.g., signature verification fails during a secret rotation), the delivery will be dead-lettered. Use the replay mechanism to re-send it once you fix the handler.
Dead-letter queue
Deliveries that exhaust all retry attempts land in a visible dead-letter queue accessible at:
Console: Settings → Webhooks → subscription name → Deliveries tab → filter by Status: Failed (permanent).
API:
curl "https://puck.acme.com/v1/webhooks/deliveries?status=failed_permanent" \ -H "Authorization: Bearer pck_live_…"Each dead-lettered delivery shows:
- The full event envelope (as it was originally sent)
- The last HTTP response code and response body (truncated to 4 KB)
- Timestamps for each attempt
Replaying a delivery
You can re-trigger a dead-lettered delivery at any time:
Console: Settings → Webhooks → subscription name → Deliveries → delivery row → Replay.
API:
curl -X POST \ "https://puck.acme.com/v1/webhooks/deliveries/<delivery_id>/replay" \ -H "Authorization: Bearer pck_live_…"A replay fires the delivery immediately with a new delivery_id but the same event_id. Your handler must deduplicate on event_id — a replayed delivery is semantically the same event arriving a second time.
Inspecting individual deliveries
Every delivery attempt is logged. To inspect the full attempt history for a subscription:
curl "https://puck.acme.com/v1/webhooks/deliveries?subscription_id=sub_018f2e3a…" \ -H "Authorization: Bearer pck_live_…"The response includes one row per delivery, with attempt_count, last_response_code, next_attempt_at (null if terminal), and the truncated last_response_body.
Tips for reliable delivery
- Respond fast, process async. Aim to respond
2xxwithin 5–10 seconds. Puck’s request timeout is 10 seconds; exceeding it counts as a transient failure and consumes retry budget. Enqueue the event for processing and respond immediately. - Deduplicate on
event_id. At-least-once delivery means your handler will occasionally receive the same event twice. Store processedevent_idvalues in a set or a database unique index. - Return
200or204, not200with an error body. The worker only inspects the HTTP status code, not the response body. A200 OKwith{"error": "..."}in the body is treated as success.
Related
- Webhooks overview — delivery model and at-least-once semantics
- Subscription management — managing subscriptions and disabling during maintenance
- Signature verification — verifying each delivery before processing