- Doesn’t return within 10 seconds
- Returns a non-2xx HTTP status (3xx, 4xx, 5xx)
- Refuses the TLS handshake
- Closes the connection mid-response
Backoff schedule
Dispatcher config —max_attempts: 6, backoffs: [5s, 30s, 5m, 30m, 2h]:
| Attempt | Wait before this attempt | Cumulative time since first attempt |
|---|---|---|
| 1 (initial) | 0s | 0s |
| 2 | 5s | 5s |
| 3 | 30s | 35s |
| 4 | 5m | 5m35s |
| 5 | 30m | 35m35s |
| 6 | 2h | 2h35m35s |
crates/dispatcher-rs/src/webhook.rs.
What “delivery succeeded” means
Any 2xx response counts as success. We don’t inspect the body. Common success patterns:Detecting missed events
If your endpoint was down for the full retry envelope and the alert ran out of retries, inspect the webhook delivery log:/v1/me/webhooks/$ID/deliveries every few minutes as a
safety net. Idempotency (using x-pegana-event-id) means you can re-process a
stale event without duplicating action.
Manual replay
If you want missed dead-letter events delivered again, hit the replay endpoint:TLS errors
If your TLS handshake fails (expired cert, mismatched SAN, weak cipher), the delivery counts as a failed attempt. We don’t differentiate handshake failures from HTTP errors in the retry schedule. If delivery history shows repeated handshake failures, you have a real TLS problem. We don’t accept self-signed certs.Timeouts
Each attempt has a 10-second deadline for the full request/response cycle. Read slow? Sleeping in your handler? Doing heavy work in-band? You’ll timeout. Recommended pattern:Self-hosted: tune the schedule
If you’re running your own Pegana instance, the retry schedule is incrates/dispatcher-rs/src/main.rs
under the WebhookConfig initialization (look for max_attempts: 6). To shorten
or extend the window, change both max_attempts and the backoff array in
crates/dispatcher-rs/src/webhook.rs.
Next
TypeScript example
Cloudflare Worker reference.
Rust example
axum reference.
Python example
FastAPI reference.