Skip to main content

Webhook Retry Logic

AfriRoute guarantees at-least-once delivery. If your endpoint does not acknowledge an event with a 2xx response, the event is retried on an exponential backoff schedule. This page explains the retry behavior and how to build a handler that stays correct under retries.

✅ What Counts as Success​

ResponseOutcome
2xxDelivery succeeded — no retry
3xxTreated as failure — redirects are not followed
4xxTreated as failure and retried (except 410 Gone, which disables retries for that event)
5xxTreated as failure and retried
Timeout (> 10s)Treated as failure and retried
Connection errorTreated as failure and retried

🔁 Retry Schedule​

AfriRoute makes up to 6 attempts over roughly 24 hours with exponential backoff and jitter:

AttemptApprox. delay after previous
1immediate
2~30 seconds
3~2 minutes
4~10 minutes
5~1 hour
6~6 hours

After the final failed attempt the event is marked undelivered and moved to a dead-letter log you can inspect and replay from the dashboard.

🔑 Idempotency Is Required​

Because the same event may be delivered more than once (e.g. your 2xx was lost in transit), your handler must be idempotent. Key off the event id.

Node.js — idempotent handler
const processed = new Set(); // use Redis/DB in production

app.post('/webhook', async (req, res) => {
const { id, event, data } = req.body; // after signature verification

if (processed.has(id)) {
return res.sendStatus(200); // already handled — ack and skip
}

try {
await handleEvent(event, data);
processed.add(id);
res.sendStatus(200);
} catch (err) {
console.error(err);
res.sendStatus(500); // signal AfriRoute to retry
}
});
Python — idempotent handler
@app.route("/webhook", methods=["POST"])
def webhook():
body = request.json # after signature verification
if seen(body["id"]):
return "", 200
try:
handle_event(body["event"], body["data"])
mark_seen(body["id"])
return "", 200
except Exception:
return "", 500 # triggers retry

🧯 Replaying Undelivered Events​

From Dashboard → Webhooks → Delivery Log you can:

  • Inspect the request, response status, and body of each attempt.
  • Manually replay an individual event or a range after fixing an outage.
  • Filter by event type, status, and time window.

You can also list recent deliveries via the API:

curl https://api.afriroute.ai/api/v1/webhooks/wh_5k2j9/deliveries \
-H "Authorization: Bearer $AFRIROUTE_API_KEY"

💡 Best Practices​

  • Respond within 10 seconds — offload slow work to a queue and ack immediately.
  • Persist idempotency keys in a durable store (Redis/DB), not in memory.
  • Return 5xx on transient failures so events are retried; return 2xx only when safely handled.
  • Return 410 Gone for endpoints you have permanently retired to stop retries cleanly.
  • Monitor the delivery log and alert when undelivered counts rise.

⚠️ Failure Codes​

CodeMeaning
WEBHOOK_TIMEOUTEndpoint did not respond within 10s
WEBHOOK_UNREACHABLEDNS/TLS/connection failure
WEBHOOK_REJECTEDEndpoint returned a non-2xx status
WEBHOOK_EXHAUSTEDAll retry attempts failed; event dead-lettered

See the error code reference for the full list.


Last Updated: May 2026 | Need help? [email protected]