Exponential backoff vs. jitter: webhook retry strategies explained
Backoff and jitter get mentioned together so often they start to sound like one idea. They're not — they solve two different problems, and a webhook sender that only implements one of them is still exposed to the failure mode the other one prevents.
Backoff: spacing out retries of one failing request
Backoff answers: a single delivery just failed — how long before I try it again? Retrying immediately assumes the failure was a fluke; retrying immediately and repeatedly assumes the receiver is fine, which is exactly wrong if it's down or overloaded. Exponential backoff increases the delay between attempts on the same failing request, so a receiver that's struggling gets increasing breathing room instead of a tighter and tighter retry loop.
Jitterflow retries a failed delivery (destination unreachable, timed out, or a non-2xx response) with exponential backoff starting at 5 seconds, up to the endpoint's maxRetries (default 5). Once the last attempt is exhausted, the job moves to the dead-letter queue instead of being dropped — see job lifecycle for every state a job passes through.
Jitter: spacing out a burst across many targets
Jitter answers a different question: I have a burst of deliveries about to go out at once — how do I stop that from looking like an attack to the receiver's rate limiter? This has nothing to do with anything failing yet. Five hundred orders finishing in the same batch job means five hundred near-simultaneous webhook calls, and most receiver APIs cap requests per second well under that — every call past the cap gets a 429 on the very first attempt, before backoff even enters the picture.
Jitterflow enforces a minimum spacing (cadenceSeconds, default 600s / 10 minutes) between consecutive deliveries to the same targetIdentifier, offset by a random amount within ±jitterRangeSec (default 90s) so many targets don't all land on the same clock tick either. A fresh target's first job still goes out almost immediately — cadence only applies to that target's next job, not to different targets sending concurrently. Full mechanics, including a worked five-call example, are on Jitter, cadence & job lifecycle.
Why you need both
| Scenario | What handles it |
|---|---|
| A batch job fires 500 webhooks at once, all to different customers | Jitter — spreads the burst so the receiver's rate limit never sees a spike |
| One delivery times out because the destination is briefly down | Backoff — retries that one delivery with increasing delay instead of hammering it |
| A destination is down for an extended outage and every retry still fails | Backoff exhausts maxRetries, then the dead-letter queue holds the job for manual replay |
A sender with only backoff still trips rate limits on the first attempt of a large burst, since nothing spread the burst out in the first place. A sender with only jitter still hammers a genuinely failing endpoint on every scheduled attempt, since nothing increases the delay after a failure. Both run automatically on every Jitterflow endpoint — jitter on the way out, backoff on retry, no config required beyond the two defaults above.