How to Replay a Stripe Webhook Safely After a Silent Failure
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.
| |
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
- Go to Developers → Events in the Stripe dashboard
- Search for the event by type (
checkout.session.completed,invoice.paid, etc.) and find the specific event by timestamp or customer - Open the event detail page
- 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:
| |
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:
| |
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
| |
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:
| |
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.
Related guides
- Stripe webhook delivered but nothing happened — determine whether replay is appropriate
- Stripe invoice paid but credits not added — recover credit grants safely
- checkout.session.completed fired but app did not update — recover access provisioning failures
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 →