
The webhook that failed once and vanished
A payment event fires while your automation is halfway through an edit. The endpoint answers 502, the sender shrugs and schedules a retry, and somewhere in the next few hours the question stops being whether the message will land and becomes whether anyone will notice it did not. Senders are better at this than people assume. The gap is almost always on the receiving side, where the payload arrives into a queue you can see but cannot control.
The sender is generous, and the generosity expires
Stripe attempts delivery for up to three days in live mode with exponential backoff, and three times over a few hours in a sandbox. That is a real commitment. It is also a window. Once it closes, recovery is a human job: clicking Resend in the Dashboard, which works for 15 days after the event was created, or running stripe events resend from the CLI, which works for 30. Both assume somebody knows which event to look for.
Stripe is equally plain about what it does not promise. Events are not delivered in the order they were generated, so a subscription signup can hand you invoice.paid before customer.subscription.created. Snapshot events record their created timestamp in whole seconds, which means two distinct events can share one, and the docs tell you to track event IDs rather than timestamps to spot duplicates. None of that is a flaw. It is a sender being honest that the receiver has to do real work.
So the interesting question is not how hard the sender tries. It is what your side does with the payload in the second after it arrives.
The replay button has conditions attached
Zapier gives you a replay, which is more than many platforms offer. Autoreplay is excluded from the Free plan, retries an errored run up to five times, and spaces the attempts at 5 minutes, 30 minutes, 1 hour, 3 hours and 6 hours. The last attempt lands roughly 10 hours 35 minutes after the first error. Manual replay stays available for 60 days, and every replayed step consumes tasks from your plan.
Then there is the clause that catches people. Zapier will not replay a run if you have since deleted, added or moved a step, or swapped the app behind an action. Think about what that means in practice. The run failed because the Zap was wrong. Fixing the Zap is the thing that makes the failed run permanently unreplayable. You get to recover the data or fix the bug, and choosing one closes the other.
Make takes a different shape with the same ceiling. Each webhook owns a queue, and without a webhook response module in the scenario, a full queue answers the sender with 400 and the body "Queue is full", while a failed rate limit check answers 429. A webhook left unattached to any scenario for more than 5 days is deactivated and starts returning 410 Gone. A 400 tells the sender its request was malformed, which was not what happened, and 410 tells it the endpoint is permanently dead. Your capacity problem leaves the building disguised as the sender's mistake.
A queue you own holds the message until it lands
Compare the numbers rather than the marketing. Cloudflare Queues allows 100 retries per message, retention configurable up to 14 days, 25GB of backlog per queue, a visibility timeout up to 12 hours for pull consumers and a retry delay up to 24 hours. Set against five attempts across ten and a half hours, that is a different category of durability, and it costs a few lines of configuration rather than a plan upgrade.
The code is smaller than people expect. Your endpoint verifies the signature, writes the raw payload to a queue, and returns 200. Stripe's own guidance says exactly that: return a 2xx before any logic that might time out. Everything that can fail now runs behind the queue, where you choose the retry policy, keep a dead letter queue for the handful that never succeed, and replay last Tuesday's failures after you have shipped the fix rather than instead of shipping it.
When we moved Stay World Class off Webflow and Xano onto Next.js, NestJS and Supabase, the headline numbers were performance: Lighthouse went from 55.91 to 91, LCP dropped 77%. The quieter win was a 4-stage ETL that carried every legacy ID across into PostgreSQL UUIDs, so nothing had to be reconciled by hand afterwards. Owning the data path is what made that possible, and it is the same property that makes a webhook recoverable.
When staying put is the right call
Plenty of webhooks carry things that are pleasant rather than load bearing. A Slack notification, a CRM tag, a row appended to a tracking sheet. Losing one of those costs you mild annoyance, and Zapier's five attempts across ten hours will absorb almost any transient outage you are likely to meet. Building a queue for that traffic is work you will not get back.
The same goes if you already run a nightly reconciliation against the source API. If a missed invoice event gets picked up by tomorrow morning's sweep, the queue is buying you hours, not correctness, and hours may not be worth the migration. Teams on Make who have put a webhook response module in the right place and watch their queue depth are in decent shape too.
The honest test is a question, not a platform. If someone asked which messages you lost last Tuesday, could you answer? If yes, your current setup is doing its job. If the answer is that you would have to ask the sender, that is the thing worth fixing, and it is usually cheaper than the replatform it sounds like.
Sources
- Stripe, "Receive Stripe events in your webhook endpoint" (automatic and manual retries, event ordering)
- Zapier Help Center, "What is replay?" (autoreplay plans, 5 attempts, intervals, 60-day limit)
- Make Help Center, "Webhooks" (webhook queues, queue full 400, 410 Gone after 5 days)
- Cloudflare, "Queues limits" (100 retries, 14-day retention, 25GB backlog, 12h visibility timeout)
- Cloudflare, "Queues pull consumers" (batch size, visibility timeout, ack and retry semantics)
