How to Monitor Stripe Webhooks
Stripe disables an endpoint after three days of failures. Here is how to know before that happens, and why a 200 response proves less than you think.
Monitoring Stripe webhooks means checking that events are being processed, not just delivered. Those are different things, and the gap between them is where most production incidents live.
Stripe attempts delivery for up to three days in live mode with exponential backoff. If your endpoint keeps failing across that window, Stripe disables it and emails you. Three days is long enough to survive a bad deploy and short enough to lose a holiday weekend.
Why your Stripe dashboard says everything is fine
The recommended handler pattern is to verify the signature, acknowledge with a 200 immediately, and process the event asynchronously. That advice is correct and it creates a blind spot.
Once you return 200, Stripe considers the event delivered and stops caring. If your queue worker is down, your dashboard shows a perfect delivery rate while nothing is being processed. Stripe has no way to know and no reason to retry.
This is the failure that gets found by a customer emailing about a subscription that never activated, usually several days later.
Step one: ping after processing, not after receiving
Put the heartbeat call at the end of the code path that actually does the work, in the queue worker, not in the controller that returns 200.
Set the monitor's expected interval to something a little longer than your quietest realistic gap in events. For most businesses that is overnight, so a 6 to 12 hour interval with a generous grace period is a sane starting point.
If your event volume is genuinely bursty, skip the interval approach and use the volume check described further down instead.
Step two: get the signature verification right
Stripe signs with HMAC-SHA256 and sends it in the Stripe-Signature header, which contains both a timestamp and the signature. The official libraries default to a 300-second tolerance between that timestamp and your server clock.
Two things break verification more often than anything else. The first is JSON middleware. Express body-parser, Laravel's input helpers and Django's request.POST all parse and re-serialise the body, which changes the bytes and invalidates the signature. You must read the raw request body.
The second is CSRF middleware. Stripe does not send a CSRF token, so the route has to be excluded in Rails, Django and Laravel. A CSRF rejection looks like a 419 or 403 in your logs and like a generic failure in Stripe's.
Note that Stripe generates a fresh signature and timestamp for every retry, so you cannot cache or compare signatures across attempts.
Step three: watch for the endpoint being disabled
The three-day disable is the single worst outcome, because once the endpoint is off, Stripe stops sending entirely and there is nothing left to fail. Silence looks identical to a quiet period.
Stripe emails the account owner before disabling. That email goes to whoever owns the Stripe account, which in most companies is not the person on call.
| Symptom | Stripe dashboard shows | What is actually wrong |
|---|---|---|
| Endpoint timing out | Retries in progress | Handler doing work inline instead of queueing |
| 400 on every event | Consistent failures | Signature verification, usually raw-body parsing |
| 403 or 419 | Consistent failures | CSRF middleware not excluded on the route |
| 200 on every event, no records | 100% success | Queue worker down; Stripe cannot see this |
| No events at all | Nothing | Endpoint disabled, or events unsubscribed |
The reconciliation check that catches everything
Heartbeats catch a dead worker. Volume checks catch a stopped stream. Neither catches a handler that processes nine events out of ten correctly.
Once a day, list events from Stripe's API for the previous 24 hours and compare the count against what you recorded. Alert on any difference. This is more work than the other two methods combined and it is the only one that does not rely on a webhook arriving to notice webhooks have stopped.
Related guides
For the receiving side in general see webhook monitoring. If payment events feed a multi-step process, business flow monitoring covers tracking them per order.