Retry Handling
How Cresora retries failed webhook deliveries and how to handle them idempotently.
Cresora automatically retries webhook deliveries that don't receive a 2xx response. Your receiver must handle retries gracefully.
Retry schedule
| Attempt | Delay after failure |
|---|---|
| Initial | Immediate |
| 1st retry | 30 seconds |
| 2nd retry | 5 minutes |
| 3rd retry | 30 minutes |
| Total | 4 attempts over roughly 35 minutes |
After the 3rd retry fails, the delivery is recorded EXHAUSTED and Cresora stops. No further attempt is made, and no notification is sent — you find out by inspecting the delivery log or by reconciling. See Failed deliveries.
The whole retry window is about 35 minutes, not hours. Any outage or deploy longer than that drops events permanently, so treat reconciliation — not retries — as your recovery path.
These are the platform defaults. GET /webhook-metadata returns the retry policy actually in force — it is derived from server configuration, so it cannot drift from behaviour the way a hardcoded table can.
What triggers a retry
5xxfrom your endpoint403,408,429, or any other4xxnot in the permanent list below- A redirect (
3xx) — Cresora does not follow redirects; register the final URL - No response within 10 seconds (timeout)
- Connection refused or DNS resolution failure
Statuses that are never retried
400, 401, 404, 405, 410 and 422 are treated as deliberate, permanent rejections. The delivery fails on its first attempt and is never retried.
This matters most during secret rotation. If your handler answers a signature it cannot yet verify with 400, that event is dropped immediately with no retry. Use 500 for a failure you expect to recover from, and reserve 400 for a body you will never accept.
Asking Cresora to slow down
If you return a Retry-After header on a retryable response, Cresora honours it in place of the ladder step above — so you can push the next attempt further out than the schedule would, for example while you drain a backlog behind a 429.
Two limits:
- Delta-seconds only.
Retry-After: 120works. The HTTP-date form is ignored, and the ladder step is used instead — so a date-formatted header silently does nothing. - Capped at 24 hours. A larger value is clamped, so a misbehaving receiver cannot pin a delivery arbitrarily far into the future.
Because a honoured Retry-After replaces the ladder delay, it can stretch the total window well past the default ~35 minutes. The 4-attempt count does not change.
Idempotent retry handling
Because Cresora retries, your handler will receive the same event multiple times. Always handle events idempotently:
import crypto from "node:crypto";
// Use event.id as an idempotency key
async function handleWebhookEvent(event) {
// Check if we've already processed this event
const existing = await db.processedEvents.findOne({ eventId: event.id });
if (existing) {
console.log(`Duplicate event ${event.id} — skipping`);
return; // Already handled; return 200 anyway
}
// Process the event
await processEvent(event);
// Record that we've handled it
await db.processedEvents.insert({
eventId: event.id,
processedAt: new Date(),
});
}Return 200 OK even for duplicate events. A retryable 4xx keeps the delivery on the ladder and produces more duplicates; a 400 or 422 drops the event outright. Neither outcome is what you want — acknowledge duplicates with 200.
Respond quickly, process asynchronously
Your endpoint must respond within 10 seconds. For complex processing, respond immediately and handle the work in a background queue:
app.post("/webhooks/cresora", async (req, res) => {
verifySignature(req); // Throws on invalid signature — respond 400
res.sendStatus(200); // Respond immediately
await queue.enqueue(req.body); // Process asynchronously
});Manual replay
You can manually replay any webhook delivery from the Partner Portal:
- Developers → Webhooks — open the endpoint's delivery log
- Find the failed delivery
- Click Replay
Replayed events have the same id as the original — the stored envelope is resent verbatim, so your idempotency logic will correctly deduplicate them.
Two limits apply:
- One delivery at a time. There is no bulk replay; you replay a specific delivery.
- 100 replays per endpoint per hour. Exceeding it returns
429with aRetry-Afterheader.
If Cresora has suspended the endpoint (see Failed deliveries), replay is rejected until you resume it.