WebhooksApril 18, 20266 min read

How to Replay a Webhook When Your Server Was Down

Your server went down for an hour. Stripe, GitHub, and Shopify kept sending webhooks. Here is how to replay the events you missed, with and without a webhook relay.

LOG STARTS AT YOUR ORIGINorigin down · nothing writtenyou can only replay what your origin managed to receiveLOG STARTS AT THE EDGEorigin down · events still persistedselect the window → replay
Replay is only as good as where your log starts.

Your database fell over at 2 AM. By the time you woke up and fixed it, three hours of webhooks had already been sent and rejected. Now you're staring at Slack wondering how many Stripe payments didn't get fulfilled and how many GitHub pushes didn't trigger the right CI job.

This post is the actual playbook for recovering missed webhooks, provider by provider, and then the pattern that makes this a one-click problem instead of a Sunday afternoon.

TL;DR

  • Stripe: manually redeliver per-event from the dashboard, or script it via the Events API. There is no bulk UI.
  • GitHub: a per-delivery redeliver button. Also one at a time unless you script the Redelivery API.
  • Shopify: retries 8 times over 4h, then the events are gone and you re-query the Orders API to reconcile.
  • One-click replay needs upstream logging before delivery, which is the whole design of a webhook relay.

Stripe

Stripe lets you resend webhook events manually from the dashboard:

  1. Go to Developers → Events in the Stripe Dashboard
  2. Click the event you want to redeliver
  3. Scroll to Webhook attempts and click Resend

This works one event at a time, and only for 15 days after the event was created. If you missed 300 events, you are clicking 300 buttons. There is a Stripe CLI command (stripe events resend <event_id>) but it still requires you to know each event ID.

Batch replaying Stripe events

There's no first-party bulk-replay UI. The workarounds:

  • Write a script that uses the Stripe API to list events in the outage window, filter by status, and POST each one back to your endpoint yourself. You're re-constructing the webhook from the event object, and it isn't quite the same shape.
  • Or, if you logged your webhooks upstream of the outage, replay from your own log

The upstream-log approach is the one production teams build eventually. It's also exactly what a webhook relay does for you.

GitHub

GitHub shows a per-delivery redeliver button in repo Settings → Webhooks → (pick webhook) → Recent Deliveries. Click Redeliver next to each one.

Same issue: one at a time. GitHub keeps deliveries for about 30 days, so you have breathing room, but a mass replay still means a script hitting the Redelivery API.

Shopify

Shopify retries 8 times across a four-hour window before giving up. After that, the only way to recover is to re-query the Shopify API for what you missed. The webhooks themselves are gone.

This one is especially painful because Shopify's retry window is by far the shortest of the big three. If your server goes down Friday night and you don't catch it until Monday, you're doing a full reconciliation from the Orders API.

The general pattern

Regardless of provider, the recovery procedure without a relay looks like:

  1. Identify the outage window. When did your server stop 2xx-ing?
  2. Pull affected events, usually a paginated API call per provider
  3. Re-fire each event by POSTing it at your own endpoint, or re-query the source of truth and sync state manually
  4. Dedupe, because your handler must be idempotent and events may have partially delivered

This takes hours. And you only get one shot: if you miss events past the retry window, they're gone.

The one-click version

The reason AnyHook exists is to turn this into one click. When you point your webhooks at in.anyhook.net/you/app:

  • Every event is persisted before delivery, encrypted at rest
  • If your server returns 5xx, AnyHook retries on exponential backoff (Free=3, Pro=5, Scale=10 attempts)
  • If your server is down long enough to exhaust retries, the event is marked failed, but the payload is still in your log
  • Open the dashboard, filter to failed events in your outage window, click Replay. That's it.

Replays don't count against your quota. They publish fresh to your endpoint with a new signature, while the original inbound body and headers are preserved, so your handler sees the event exactly as the provider sent it.

The API equivalent is a single POST:

curl -X POST https://anyhook.net/api/v1/apps/stripe-prod/replay-failed \
  -H "Authorization: Bearer $ANYHOOK_KEY" \
  -H "Content-Type: application/json" \
  -d '{"since": "2026-04-18T02:00:00Z", "limit": 100}'

One call, up to 100 events replayed, no dashboard clicking.

Design implication

What makes replay cheap is persistence at the edge, before delivery. If your webhook log starts at your origin, you can only replay what your origin received. If your log starts at the network edge, before your origin has a chance to fail, you can replay anything that ever showed up.

LOG STARTS AT YOUR ORIGINorigin down · nothing writtenyou can only replay what your origin managed to receiveLOG STARTS AT THE EDGEorigin down · events still persistedselect the window → replay

Move logging upstream, make replay idempotent, and your worst webhook outage becomes a one-minute recovery instead of a weekend.

Two follow-ons worth reading before you need them. "Make replay idempotent" is the load-bearing half, and it is harder than a dedupe table once retries arrive concurrently: You Received the Same Webhook Twice. And if Stripe already gave up and disabled your endpoint, replay isn't available at all and you're backfilling from the Events API inside a 30-day window: Stripe Disabled Your Webhook Endpoint.

Try it: anyhook.net.

All postsApril 18, 2026 · 6 min

Stop losing webhooks.

Change one URL. Get retries, event log, and one-click replay.