Stripe Webhook Didn't Fire — Debugging Checklist (2026)

Customer paid. Stripe Dashboard shows the charge succeeded. Your handler never ran. No row in the database, no email sent, no log line. Below: the seven silent failure modes ranked by frequency, plus the exact Stripe Dashboard fields that tell you definitively whether Stripe even attempted delivery.

Before you debug: open https://dashboard.stripe.com/workbench/webhooks → click your endpoint → look at the latest event row. If it says Pending, Stripe is queued but hasn't delivered yet (rare, but happens during incidents). If it says Succeeded with 200, the event reached your handler — your bug is downstream of the webhook (database write, email send, etc). If it says Failed, you have the actual error code in the response body.

The 7 reasons your webhook didn't fire

REASON 01 — most common

Endpoint registered for test mode, charge happened in live mode

Stripe has two completely separate webhook endpoint configurations: test mode and live mode. The Dashboard top-right "View test data" toggle determines which set you see. If you registered your endpoint while toggled to test mode, your live-mode charges have nothing to deliver to.

Check: Dashboard → toggle to live mode → Developers → Webhooks. If your endpoint isn't listed, that's the bug.

Fix: click Add endpoint while in live mode. Use the same URL. Update STRIPE_WEBHOOK_SECRET in your env vars to the new endpoint's signing secret (live mode has a different secret from test).

REASON 02

Endpoint exists but no event types selected

When you register an endpoint, Stripe asks which event types to send. If you forgot to check checkout.session.completed (or whichever event your handler expects), the endpoint exists but your specific event silently won't deliver.

Check: Dashboard → Webhooks → click endpoint → "Events to send" list.

Fix: click Update endpoint → add the missing event type. Default to "Send all events" while debugging — you can filter later.

REASON 03

Endpoint URL is wrong

Typo in the URL when registering. Common mistakes: https://sentryforge.royalruby.io/webhook vs https://sentryforge.royalruby.io/api/stripe-webhook vs https://www.sentryforge.royalruby.io/api/stripe-webhook. Stripe attempts delivery to the URL you gave it; if that path doesn't exist on your origin, your handler never runs.

Check: Dashboard → Webhooks → click endpoint → "URL" field. Curl that URL: curl -I https://your-url-here/path. If it 404s or redirects to a static page, that's the bug.

Fix: click Update endpoint → fix the URL. Stripe will retry pending events automatically once the URL works.

REASON 04

Origin is blocking Stripe's IP range

If you put Cloudflare, Akamai, AWS WAF, or any firewall in front of your endpoint with strict bot-protection rules, Stripe's delivery requests can get blocked. Stripe will retry the request and log it as failed in Dashboard, but your origin never sees the request.

Check: Dashboard → Webhooks → click endpoint → "Recent deliveries" panel. If you see entries with Failed status and a 403/503 response code, your CDN or WAF is the gatekeeper.

Fix: allowlist Stripe's webhook IPs in your firewall (stripe.com/docs/ips). Or, easier: use Stripe's signed webhook secret as auth and turn off IP-based blocking for the webhook path.

REASON 05

Handler responds 4xx/5xx and Stripe gave up

Stripe retries failed deliveries on a schedule (immediately, then 5min, 1hr, 4hr, 12hr, 24hr, then drops). After 3 days of failures, Stripe stops trying. If your endpoint was broken for a week then fixed, anything older than 3 days is gone — Stripe won't replay it.

Check: Dashboard → Webhooks → click endpoint → "Recent deliveries" → look for events older than 3 days with Failed status.

Fix: manually replay each failed event using Dashboard → click event → "Resend." Or write a one-shot backfill that pages through events.list and re-emits each through your handler. See our idempotency post for safe re-emission patterns.

REASON 06

Endpoint disabled because of repeated failures

If your endpoint failed 100% of deliveries for 24+ hours, Stripe automatically disables it to stop the retry storm. Status changes from enabled to disabled. Future events go nowhere.

Check: Dashboard → Webhooks → endpoint status pill. If it says Disabled, this is your bug.

Fix: click Enable. Then audit the deliveries that failed during the dead window — those events will need manual replay (see Reason 05).

REASON 07

You're listening on the wrong endpoint object

Connect platforms have two webhook setups: account-level (events about your account) and Connect-level (events about your connected accounts' charges). If you only registered an account-level endpoint, you'll never see charge.succeeded for charges happening on connected accounts.

Check: Dashboard → Webhooks → look for "Connect endpoints" tab. If empty and you're a Connect platform, that's the gap.

Fix: register a Connect endpoint. Use the same URL but you'll need to differentiate inside your handler with the account field on the event payload.

The dashboard field that solves this in 30 seconds

Most teams skip the obvious first move: open the event in Stripe Dashboard and look at Webhook attempts. That panel shows:

If "Webhook attempts" is empty, your endpoint isn't registered for the right mode/event/account combination (Reasons 1, 2, 7). If it shows attempts with HTTP 200, your handler ran and the bug is downstream. If it shows attempts with 4xx/5xx, the response body usually contains the actual error message your code logged.

Direct link template: https://dashboard.stripe.com/events/<evt_xxx> — paste the event ID from the charge to land directly on the diagnostic panel.

What to do after you find the cause

  1. Fix the root cause (re-register endpoint, allow IP, etc).
  2. Replay the failed events using Dashboard → "Resend" on each one.
  3. Set up monitoring so this doesn't surprise you again — see the next section.

Stop finding out from customers

Webhook Health Check pings your endpoint every 5 minutes and emails you within 60s if it returns non-2xx. $29/mo. The simplest possible monitor — designed specifically for indie hackers running one Stripe endpoint who don't want to learn Hookdeck's $39/mo dashboard or pay Svix $490.

See the offer →

If you want to skip the debugging

Sentry Forge ships your Stripe webhook with all 7 of these failure modes pre-handled — endpoint registered for the right mode, all event types selected, HMAC verification, 3-day retry-replay queue, and Stripe-IP allowlist documented in the deploy guide. $199 flat, 24-hour delivery.

Related