stripe webhook endpoint 400 event type not handled
For engineers running Stripe webhooks in an onboarding flow. Use when the dashboard shows deliveries failing with 400 because the handler does not recognize the event type. Key triggers: new Stripe event types, endpoint subscribed to all events, strict handler switch. Not for 401 signature failures or 500 handler crashes.
Stop Stripe webhooks failing with 400 on unhandled event types
TL;DR
The endpoint returns 400 for event types it does not recognize, and Stripe treats any 400 as a failed delivery, so it retries for days. Return 200 for unknown event types (log and acknowledge them) and reserve 400 and 500 for genuinely broken requests. One unhandled event type can generate hundreds of pointless retries and bury real failures.
The error
Stripe Dashboard / Developers / Webhooks: delivery attempts failing with 400
Endpoint logs: "Event type [payment_intent.x] not handled", retried dozens of timesUse this skill
Agent? Search Vectle for this error:
curl -s "https://vectle.com/api/v1/search?q=stripe%20webhook%20endpoint%20400%20event%20type%20not%20handled"Fix it
Step 1: Add a default branch that acks unknown types
In the webhook handler's event-type switch, add a default case: log the event type and id, then return HTTP 200.Expected: unknown types stop 400ing immediately on deploy. The handler now distinguishes "not interested" from "broken".
Step 2: Align dashboard event selection with the handler
In the Stripe dashboard, open the webhook endpoint and compare the selected events with the types the code handles.Expected: either the selection matches the code, or the default branch covers the gap. Subscribing to all events with a strict handler is what caused this.
Step 3: Redeploy and watch the retries drain
Deploy the fix and watch the webhook deliveries page through the next retry cycle.Expected: failing deliveries flip to 200 and the retry queue drains. Stripe stops retrying once it gets 2xx.
Step 4: Log unhandled types visibly
Emit a structured log line (or metric) per unhandled event type so new Stripe event types surface in dashboards instead of passing silently.Expected: visibility without failures. When Stripe ships a type you actually want, you will see it in the logs first.
Step 5: Keep 400 for genuinely bad requests
Continue returning 400 for failed signature verification and malformed payloads.Expected: real problems still signal correctly. Only the "I do not care about this event" case changed to 200.
When this applies
- Webhook deliveries fail with 400 on unrecognized event types
- The retry queue keeps growing for one event type
- The endpoint subscribes to all events but handles a few
When it doesn't
- Deliveries fail with 401 (signature verification problem)
- The handler 500s on known event types (code bug)
- Events never arrive at all (URL or network issue)
Compatibility
Stripe webhooks, any web framework. Same principle applies to most webhook providers.
Variant phrasings
stripe webhook 400 unhandled event
The default-ack branch is the fix regardless of which event type is unhandled.
stripe retrying webhook event not handled
Stripe retries non-2xx for days with backoff. The 200-ack stops the storm, not the dashboard.
Why it happens
Stripe's delivery contract is simple: 2xx means received, anything else means retry. A handler that 400s on "event type I do not handle" converts indifference into failure, and Stripe dutifully retries the indifference for days. The endpoint was written as if 400 means "bad event", but to Stripe it means "try again later".
Edge cases
- Signature verification must run before the 200-ack; never ack an unverified payload
- Test mode and live mode have separate event selections; fix both
- Stripe adds new event types over time; the default branch is future-proofing, not laziness
If it still fails
- Capture the exact timestamp, the username, and the full error from the system log before changing anything else.
- Reproduce with a single test user so you are not debugging a crowd.
- If it worked before, diff the config against the last known good, then open a vendor ticket with the timestamp and request id. Never send secrets or private keys.
Prevention
- Track credential and certificate expiry with alerts, not memory.
- Run a synthetic check per app daily so breakage pages you, not a user.
- Document mappings, URLs, and runbook steps where the next admin will find them.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_zboyRoNlSvNz7O470Hj4DA