CheckoutAlarmWatch my app free

Stripe webhook 400, 500 or timed out: what each one means

Your webhook endpoint shows red errors in Stripe. A 400, a 500 and a timeout look alike, but each has a different cause. Find yours below.

Free tool: paste your webhook error and get the fix

What it would catch here

All three are failed deliveries. With a read-only key, CheckoutAlarm emails you when events are still undelivered about 15 minutes after they were created.

What it won't catch

It won't say which code you got or which line crashed. Read that in Stripe's Event deliveries tab and your logs. If your code answers 200 and then fails, Stripe sees a success and so do we.

Plans

Free: 1 site, checked once a day. Pro: $19 a month, up to 3 sites checked hourly. You prove you own the site first. For Stripe we only take a restricted key with read access to Events, Webhook Endpoints and Charges. Full secret keys are refused. Compare plans

Open the failed delivery

In Workbench, go to Webhooks, pick the endpoint, then Event deliveries. Click a failed event to see its status code and your server's reply. Stripe: view event deliveries

Most often: signature check

Stripe lists 400 as "the destination URL returned an error." In Stripe's sample code, a failed signature check returns 400. Stripe: fix HTTP status codes

Fix the signature

Check with the raw body, not parsed JSON. Use the whsec_ secret for this endpoint and this mode. Our signature guide walks through it.

Read your logs

For a 5xx, Stripe says the destination had a problem and to check your logs for that time. On Vercel, that is the project's Logs. On Supabase, the function's logs. Stripe: fix HTTP status codes

Common crashes

A missing setting such as an empty secret, a database call that fails, or code that expects a field the event doesn't have. Paste the log line into your AI builder and ask it to handle that case.

Answer first, work later

Stripe says to return a 2xx "before any complex logic that might cause a timeout." For timeouts, Stripe says to "defer complex logic and return a successful response immediately." Stripe: quickly return a 2xx Stripe: timed out

Next.js: use after()

Next.js after() runs code once the response is sent, inside Route Handlers. On Vercel it uses waitUntil and still has to finish within the route's max duration. Next.js: after

Supabase: use EdgeRuntime.waitUntil

Edge Functions can run work after the reply with EdgeRuntime.waitUntil(promise). It is still limited by the function's time, CPU and memory limits. Supabase: background tasks

Heavy work: use a queue

Stripe suggests an asynchronous queue to handle bursts of events. Stripe: process events asynchronously

A 200 doesn't mean it worked

Once you answer 200, Stripe marks the event delivered and stops retrying, even if your later work fails. Save the event id and check that each one finished. Stripe also says to log processed event ids so duplicates are skipped. Stripe: handle duplicate events

What does a 400 on a Stripe webhook usually mean?
Stripe lists 400 as 'the destination URL returned an error.' The most common cause is a signature check that fails, often because the body was changed before checking, or the wrong whsec_ secret is set.
What does a 500 mean?
Your code crashed or threw an error. Stripe tells you to check your server's logs for that time.
Why does my webhook time out?
Your code took too long to answer. Stripe's advice is to answer with a 2xx right away and do the slow work after.
Will Stripe retry?
Yes. In live mode Stripe retries for up to three days. Make your handler safe to run twice, since retries and duplicates happen.

CheckoutAlarm emails you when Stripe events stay undelivered after a 400, a 500 or a timeout. It can't see work that fails after you already answered 200.

CheckoutAlarm is new. It only checks sites you prove you own. How to verify

Watch my app free

Docs checked on Oct 20, 2026.