To monitor Stripe webhook failures, verify every delivery's signature, return a 2xx fast, log processed event IDs, and watch three signals: signature rejections at your endpoint, events Stripe created that never reached you, and events Stripe is still retrying. Stripe's Event deliveries tab shows each attempt; MRRdue's Webhook Doctor watches the webhook feeding your MRRdue workspace and alerts when that pattern breaks.
Webhooks fail quietly. Stripe keeps charging customers, your code simply stops hearing about it, and the first symptom is often a revenue number or an account status that is wrong days later. The rest of this guide covers what breaks, what Stripe does about it on its own, and what you have to watch yourself.
The five ways a Stripe webhook breaks
Signing secret mismatch. An environment variable is wiped or replaced on a redeploy, the endpoint's secret is rolled in Stripe, or a test-mode secret is used for live events (Stripe issues a different secret per endpoint and per mode). Every delivery then fails verification and your handler rejects it.
Endpoint errors (4xx or 5xx). The route moved, authentication or CSRF middleware blocks the POST, the handler crashes, or the URL redirects. Stripe treats a redirect as a failed delivery, not a success.
Timeouts. The handler does slow work (database writes, emails, third-party calls) before it responds, and Stripe records the attempt as timed out. Stripe's guidance is to return a 2xx before any complex logic.
Disabled endpoint or deselected event type. If an endpoint is disabled or deleted, or an event type is removed from it, Stripe stops sending those events. Stripe also stops retrying an event if the endpoint is disabled or deleted when the retry falls due.
Received but never processed. Your endpoint returned 200, so Stripe marks the event delivered, but the job that was meant to act on it failed afterwards. This one never shows up in Stripe's dashboard, because from Stripe's side nothing went wrong.
Verify signatures on the raw request body
Stripe signs every event in the Stripe-Signature header with an HMAC-SHA256 signature made with your endpoint's whsec_ signing secret. Verify it with Stripe's official library, passing the raw request body exactly as received. A framework that parses or re-serialises the body first will make every signature fail.
The header includes a timestamp to limit replay attacks, and Stripe's libraries apply a default tolerance of five minutes, so keep your server clock synced. When you roll a secret, Stripe can keep the old one active for up to 24 hours and signs each delivery with both. Deploy the new secret inside that window.
How Stripe retries failed deliveries
Stripe's webhook documentation says that in live mode it retries a failed delivery for up to three days with exponential backoff. Events created in a sandbox are retried three times over a few hours. The endpoint's Event deliveries tab shows when the next retry is due.
You can also resend an event by hand: from the Dashboard for up to 15 days after the event was created, or with stripe events resend in the Stripe CLI for up to 30 days. A manual resend does not cancel Stripe's automatic retries. Stripe also does not guarantee that events arrive in the order they were created. These are Stripe's published behaviours at the time of writing; check the official docs before relying on exact numbers.
Make your handler idempotent
Your endpoint will sometimes receive the same event more than once, and a retry after a timeout can arrive while the first attempt is still running. Record the IDs of events you have processed and skip any you have already seen. Do not use the event's created timestamp to deduplicate or order events: it is only accurate to the second.
Where order matters, re-fetch the current object from the API instead of trusting the payload sequence. Stripe also recommends handing events to an asynchronous queue, so that a renewal spike at the start of the month cannot overwhelm the endpoint and turn into timeouts.
Read the delivery log in the Stripe Dashboard
In Workbench, open Webhooks, select the endpoint, then the Event deliveries tab. Each event is listed as Delivered, Pending or Failed. Opening one shows the HTTP status your endpoint returned and when the next attempt is scheduled.
Stripe's status table maps the usual errors: unable to connect (the host is not publicly reachable), 3xx (a redirect, counted as a failure), 4xx (access restrictions or a missing route), 5xx (your application errored), TLS errors (certificate problems; Stripe requires TLS 1.2 or later) and timed out (respond sooner, process later).
Alert thresholds that catch problems early
Alerting on every failed attempt is noisy, because Stripe retries and many failures heal themselves. Alert on patterns instead. These are the defaults MRRdue's Webhook Doctor ships with, and they make a reasonable starting point for your own monitoring.
Three or more signature rejections in a check window: the secret has almost certainly broken. Two or more missing events that Stripe created over 10 minutes ago, among the event types you actually handle, within a 90-minute lookback: deliveries have stopped. Any event still pending delivery after 30 minutes: Stripe is stuck retrying. Any event received but not processed within 15 minutes: a silent downstream failure.
How MRRdue's Webhook Doctor detects these
Every MRRdue workspace receives your Stripe events at its own webhook endpoint, so MRRdue can watch that webhook directly. Webhook Doctor is included on every plan and checks each connection on the scheduler heartbeat, about every 30 minutes.
On each check it counts signature rejections recorded at the endpoint, looks for events received but failed or stuck in processing, compares Stripe's own event list with what MRRdue actually received (filtered to the event types MRRdue acts on), and flags events Stripe still shows as pending delivery. A problem opens an incident with the likely cause and the fix. The alert goes out by email or Slack, with reminders after one hour, six hours later, and then once a day until it resolves.
Limitations
Webhook Doctor watches the webhook that feeds your MRRdue workspace, not your own application's webhook endpoints. A rotated secret or a platform outage often hits both, but MRRdue cannot see your app's handler or its responses.
The Stripe event-list comparison needs read access to Events on your restricted key, which is optional. Without it, Webhook Doctor still runs the signature and unprocessed-event checks. MRRdue never sees the HTTP status Stripe received, so endpoint trouble is inferred from missing deliveries and Stripe's pending-delivery count. Checks run on a schedule, not in real time. On Dodo Payments connections, the Stripe event-list comparison does not apply.
Frequently asked questions
How long does Stripe retry a failed webhook? In live mode, Stripe's docs describe up to three days of retries with exponential backoff; in a sandbox, three attempts over a few hours.
Why does signature verification fail after a deploy? Usually the signing-secret environment variable changed, the endpoint's secret was rolled, a test secret is checking live events, or the framework altered the raw body before verification.
Does a 200 response mean the event was processed? No. It only means your endpoint accepted the delivery. If the real work happens after the response, track its outcome separately. That is the failure Stripe's dashboard cannot show.
Can I replay events I missed? Yes. Use Resend in the Dashboard for up to 15 days after an event was created, or the Stripe CLI for up to 30 days.
Can MRRdue monitor my own app's webhook endpoint? No. Webhook Doctor monitors the Stripe webhook that delivers events to your MRRdue workspace, using the read-only key you already connected.