01 · How it works, in 6 steps
How does a retry policy work?
- 1
Classify every failure
Map errors to retry, retry-later, fix-then-retry and never-retry. Branch on the Graph API
error.codein the JSON body rather than the HTTP status alone, and treat timeouts as transient but with an unknown outcome. - 2
Set an attempt cap and a deadline
Allow a few attempts — three to five is common — inside an overall time limit set by what the work is worth: seconds for an OTP, minutes for a dispatch notice, hours for a non-urgent report.
- 3
Space attempts with backoff and jitter
Delay each retry exponentially with random jitter so clients that failed together do not retry together, and honour waits the error implies, such as 24 hours for 131049.
- 4
Enforce a retry budget
Track retries as a share of all requests and stop retrying when the share passes a threshold such as 10%. When most calls are failing, more retries only add load.
- 5
Make retries safe
Only retry operations that are idempotent or carry an idempotency key. For sends, carry your own ID in
biz_opaque_callback_dataand check status webhooks before resending after a timeout. - 6
Give up loudly
When attempts or budget run out, move the job to a dead-letter queue with the last error and alert on it, instead of dropping it silently.
02Try it
Hands-on: use the controls to work through it yourself.
Step through a retry policy until it gives up
Retries exhausted
t = 10.7 s
- Attempt 1 at 0 ms: failed.
- Attempt 2 at 173 ms, after waiting 173 ms: failed.
- Attempt 3 at 2.08 s, after waiting 1.91 s: failed.
- Attempt 4 at 3.85 s, after waiting 1.76 s: failed.
- Attempt 5 at 10.7 s, after waiting 6.87 s: failed.
- Dead-letter the job and alertafter 5 attempts · t = 10.7 s
The delay formula
delayn = min(cap, base × factorn)
All 4 retries are spent (5 attempts in 10.7 s), so the client stops: Dead-letter the job and alert.
24 clients, same outage · retry n = 3
Full jitter spreads their retries across the window: at most 5 land together, so the recovering server is not flattened.
Gave up after 5 attempts at 10.7 s: Dead-letter the job and alert.
03What is a retry policy?
A retry policy is the set of rules your code follows when a call fails: whether to try again at all, how many attempts to allow, how long the whole effort may take, and when to give up and hand the job to a dead-letter queue or a person. It is the decision layer of error handling. Exponential backoff — how long to wait between attempts — is a separate mechanism that the policy uses.
The core of a good policy is classification. Some failures are transient: a timeout, a dropped connection, an HTTP 5xx, or a rate limiting error from the WhatsApp Cloud API such as error 130429 (rate limit hit), returned when the number's throughput is exceeded, or 131056 (too many messages to the same recipient too quickly). Waiting and retrying can fix these. Others are permanent for that request: an invalid parameter (100), a free-form message outside the customer service window (131047), template parameters that do not match (132000), a template that does not exist (132001) or a policy block (368). Retrying those changes nothing except your error count.
A third group needs an action before any retry. An expired or invalid access token (190) needs a new token first. A per-user marketing limit (131049) should not be retried for at least 24 hours. A paused template (132015) needs the template to recover or a substitute. A policy also needs limits — a maximum number of attempts per request and a retry budget across all requests — because retries are extra load, and they arrive exactly when a dependency is struggling.
Key takeaways
- A retry policy decides whether a failed call is tried again, how many times, and inside what overall time and load budget.
- Retry transient failures — timeouts, 5xx responses, throughput error 130429 — and never retry errors caused by the request itself.
- Errors such as 100 (invalid parameter), 131047 (re-engagement) and 132000 (parameter mismatch) fail identically on every attempt.
- Cap attempts per request and keep retries under about 10% of total traffic, so a struggling dependency is not buried in retries.
- The policy is the go/no-go decision; exponential backoff and jitter only set the delay between the attempts it allows.
04 · Key facts
A retry policy: key facts and limits
- Retry (transient)
- Timeouts, 5xx, 130429, 131000, 131016
- Never retry as-is
- 100, 131047, 132000, 132001, 368
- Act first, then retry
- 190: new token · 132015: wait for the template
- Per-user marketing limit (131049)
- Wait at least 24 hours
- Common attempt cap
- 3–5 attempts per request
- Common retry budget
- Retries at most 10% of requests
- Not your policy
- Meta retries failed webhooks for up to 7 days
05 · In code
A retry policy in code
// Turn a failed Cloud API call into an action.const RETRY = new Set([1, 2, 4, 80007, 130429, 131000, 131016, 131057]);const NEVER = new Set([100, 131047, 131051, 132000, 132001, 368]);export function classify({ status, error, timedOut }) {if (timedOut) return { action: "check-status-then-retry" }; // outcome unknownconst code = error?.code;if (code === 190) return { action: "refresh-token-then-retry" };if (code === 131049) return { action: "retry-after", waitMs: 24 * 3600 * 1000 };if (code === 131056) return { action: "slow-down-for-this-recipient" };if (code === 132015) return { action: "hold-until-template-active" };if (RETRY.has(code) || status >= 500) return { action: "retry-with-backoff" };if (NEVER.has(code)) return { action: "dead-letter" };return { action: "dead-letter", unknownCode: code }; // do not guess}
One table, used by every sender. Unknown codes go to the dead-letter queue: new error codes are more often permanent than transient.
06 · Why it matters
Why does a retry policy matter?
Retries are the cheapest reliability feature you can add and the easiest to get dangerously wrong. Without them, every network blip becomes a lost order update. With a naive 'retry everything five times', permanent errors waste capacity and delay alerts, throttling errors are hammered harder, and an outage turns into a retry storm that keeps the service down. A deliberate policy recovers transient failures automatically and sends everything else straight to the right handling.
07 · Mistakes to avoid
Common mistakes with a retry policy
Deciding on the HTTP status alone
Graph API errors carry the detail in the JSON
error.codeanderror_data.details; two responses with the same status can need opposite handling. Read the code before deciding.Retrying the request's own mistakes
Invalid parameters, closed service windows and template mismatches fail the same way every time. Retrying them burns throughput and hides the bug; dead-letter them immediately.
Retries without a budget
When a dependency is down, unbounded retries multiply traffic and keep it down. Cap attempts per request and retries as a share of all traffic, and let a circuit breaker stop calls entirely while the dependency recovers.
Blind retries after a timeout
A send that timed out may have been delivered. Retrying without a correlation check produces duplicate messages, and every duplicate that is delivered is billed again.
Stacking retries at every layer
If the HTTP client, the worker and the queue each retry three times, one failure becomes 27 calls. Retry at one layer and make the others fail fast.
08Example: Sorting a festive campaign's failures
A Tiruppur apparel brand sends 400,000 marketing templates over a Diwali evening and about 2,000 calls fail. The retry policy sorts them. Most are 130429 throughput errors from the opening burst, retried with jittered backoff and accepted within minutes. A few hundred are 131049 per-user marketing limits, scheduled for no earlier than 24 hours later instead of being hammered. A batch of 132000 errors from one segment's broken variable mapping goes straight to the dead-letter queue and pages the developer. A handful of timeouts are checked against status webhooks before any resend. Nothing that could not succeed is retried, and nobody receives the offer twice. (Figures are illustrative.)
09Retry Policy FAQs
Which WhatsApp API errors should be retried?+
How many times should an API call be retried?+
What is the difference between a retry policy and exponential backoff?+
What is a retry budget?+
Is this the same as Meta's webhook retry policy?+
10 · Pro tips
Best practices for a retry policy
- Keep the classification table in one shared module, with a unit test for every error code it names.
- Log the attempt number and the chosen action on every failure, so dashboards separate retries from first attempts.
- Treat unknown error codes as dead-letter, not retry, and review them weekly.
- Revisit the table whenever Meta documents new Cloud API error codes.