How to Replay a Stripe Webhook Safely After a Silent Failure

SendPromptly
5 min read

Stripe stores every event and lets you resend it to your endpoint — either from the dashboard or programmatically via the API. This is one of the most useful tools available when a webhook was delivered but not processed correctly.

The catch is that replaying a webhook is only safe if your handler is idempotent. If it is not, you risk double-provisioning access, adding credits twice, or firing duplicate downstream actions. This guide covers how to verify safety, replay the event, and confirm the fix.

When to Replay vs. When Not To

Replay is appropriate when:

  • Stripe shows the event was delivered, but your app has no record of it (a missed delivery)
  • The event was processed by your handler, but an error prevented the business effect from being applied
  • Your workers were down at the time of delivery and the event was never consumed from the queue

Replay is not the right fix when:

  • The event was processed and the business effect was applied — replaying will cause duplicate effects if your handler is not idempotent
  • The underlying bug has not been fixed — replaying into a broken handler will fail again
  • You are not sure whether the event was processed — investigate first, replay second

Before You Replay: Verify Your Handler Is Idempotent

An idempotent handler produces the same result whether it runs once or ten times. For Stripe webhook handlers, this means:

Check 1: Do you deduplicate on Stripe event ID?

Your handler should check whether the event ID has already been processed and skip if it has. Stripe can deliver the same event more than once, and replaying will deliver it again.

1
2
3
4
5
6
# Example pattern (pseudocode)
if ProcessedEvent.exists(stripe_event_id=event.id):
    return 200  # Already handled, skip safely

# ... apply business effect ...
ProcessedEvent.create(stripe_event_id=event.id)

Check 2: Is the business effect safe to apply twice?

If no deduplication exists, ask: what happens if the handler runs twice?

  • Provisioning access: safe if you check current state before granting (upsert, not insert)
  • Adding credits: not safe unless you check whether credits for this payment have already been added
  • Sending a “welcome” email: not safe unless you check send history

Fix the idempotency gap before replaying.

For the database patterns behind those checks, read the Stripe webhook idempotency guide before running a replay.

How to Replay a Single Event from the Stripe Dashboard

  1. Go to Developers → Events in the Stripe dashboard
  2. Search for the event by type (checkout.session.completed, invoice.paid, etc.) and find the specific event by timestamp or customer
  3. Open the event detail page
  4. Click Resend (top right of the event page)

Stripe will deliver the event to all active endpoints registered for that event type, using the same payload as the original delivery. You will see a new delivery attempt appear under the event’s delivery history.

Limitation: The Stripe dashboard resends to all registered endpoints. If you have multiple endpoints (staging and production), all of them will receive the replay. Make sure your staging environment handles unexpected events gracefully.

How to Replay with the Stripe CLI

For programmatic replay — useful when fixing many missed events at once — use the Stripe CLI:

1
stripe events resend evt_xxx --webhook-endpoint=we_xxx

The --webhook-endpoint flag targets a specific registered endpoint. This is useful when you have separate production and staging endpoints and do not want to resend to every destination.

If you are replaying into a local listener while testing, run:

1
stripe events resend evt_xxx

Check the delivery history after the resend and then verify the app-side effect directly.

How to Replay Multiple Events

If a worker outage caused a batch of events to be missed over a time window, you need to identify and replay all affected events.

Step 1: List events in the affected time range

1
2
3
4
stripe events list \
  --type checkout.session.completed \
  --created[gte]=1716000000 \
  --created[lte]=1716003600

Replace the timestamps with Unix timestamps corresponding to your outage window.

Step 2: Cross-reference against your database

For each event returned, check whether your database shows the expected effect (access granted, credits added, etc.). Events with missing effects are your replay candidates.

Step 3: Replay individually or in bulk

Replay each missing event ID, ideally with a short delay between requests to avoid overloading your handler:

1
2
3
4
for id in evt_aaa evt_bbb evt_ccc; do
  stripe events resend $id
  sleep 1
done

Verifying the Replay Worked

After replaying, confirm that the business effect was actually applied — not just that the endpoint returned 200.

Check:

  • Does the customer’s account now show the correct plan, access, or credit balance?
  • Does your idempotency log now contain the event ID with a success state?
  • Are there any new errors in your application logs from the time of the replay?

A 200 response from your endpoint means receipt. Verify fulfillment by checking your application state directly.

For bulk incident windows, use Stripe payment reconciliation to decide which events actually need replay.

What to Do If the Replay Failed Again

If the replay returned an error or the business effect still was not applied, the underlying problem was not fixed. Common reasons:

  • Your fix was deployed but the handler still has a bug at the specific path for this event type
  • The event references a customer or subscription that no longer exists in your database
  • Your idempotency logic is now blocking the replay because a previous failed attempt wrote a record

For events that reference a deleted customer or expired session, replay will not work — you need to apply the business effect manually (or via a signed, audited repair action) based on your own records.


A safe webhook replay is the right fix for most missed delivery incidents. But building the infrastructure to detect which events need replaying — and to know whether the replay actually fixed the customer’s state — is a separate problem. SendPromptly handles the detection and gives your team a repair workflow that produces a verified outcome. Learn how repair works →