Stripe webhooks are HTTPS endpoints you register so Stripe can push event data to your app the moment something happens in your account, such as a bank confirming a payment, a customer disputing a charge or a subscription renewing. You create the endpoint in the Webhooks tab in Workbench, choose the events you want and verify every request with that endpoint's signing secret.
Two things trip people up. First, Stripe doesn't guarantee events arrive in the order they happened, so a new subscription can deliver invoice.paid before you've seen customer.subscription.created, and your code mustn't rely on the sequence. Second, signature checks need the raw request body. If your framework parses or changes it first, verification fails every time, however correct your secret is.
What Stripe sends and where
Each event arrives as a JSON payload over HTTPS. You can point one endpoint at many event types or set up separate endpoints for specific events, up to 16 event destinations in all. Registered endpoints must be publicly accessible HTTPS URLs, and Stripe can also send events straight to Amazon EventBridge or Azure Event Grid.
When you create a destination you choose a payload format: Thin, which Stripe recommends for most new integrations, or Snapshot, which carries the full object as it was at the time. Snapshot suits third-party tools that need the complete Event object. The structure follows the API version on your account when the event happened, and events can't be changed after they're created.
Creating an endpoint in Workbench
- Open the Webhooks tab in Workbench and click Create an event destination.
- Select Your account to listen to events from your own account.
- Choose Thin or Snapshot. For Snapshot, also pick the API version.
- Select only the event types your integration needs.
- Choose Webhook endpoint as the destination type, then enter the endpoint URL and an optional description.
- On the settings page, click Reveal secret and copy the signing secret that begins with whsec_.
Testing on your own machine
You don't need a live URL to start. Install the Stripe CLI, run stripe login, then run stripe listen with the --forward-to option pointing at your local endpoint. The command prints a signing secret to use in your handler. That option only works in a terminal, not in the Workbench Shell. To fire a test event, run stripe trigger payment_intent.succeeded, or create the object by hand in the Dashboard. A tunnelling tool such as ngrok is the other route if you want a temporary public HTTPS address.
While you're developing locally the endpoint can use HTTP. Once it's public, it must use HTTPS with a valid certificate, and Stripe only supports TLS versions 1.2 and 1.3.
What your handler has to do
- Accept POST requests carrying a JSON payload.
- Verify the request using the raw payload, the Stripe-Signature header and the whsec_ secret, ideally with Stripe's official libraries.
- Return a 2xx status straight away, before any slow work. Stripe's example: return 200 before you mark an invoice as paid in your accounting system.
- Hand the real work to an asynchronous queue, so a spike such as the start of the month, when subscriptions renew, doesn't swamp your server.
- Log the event IDs you've processed and skip repeats, because the same event can arrive more than once.
If you're on Rails, Django or a similar framework, you may need to exempt the webhook route from CSRF protection, or legitimate events get rejected. And don't use an event's created time to decide order or spot duplicates: distinct events can share a timestamp.
Retries and delivery errors
In live mode, Stripe retries a failed delivery for up to three days with exponential back-off. In a sandbox it retries three times over a few hours. You can resend an event yourself from the Dashboard for up to 15 days after it was created, or with the Stripe CLI for up to 30 days. A manual resend doesn't cancel the automatic retries, even if it succeeds.
| Delivery result | What it means, as of October 2026 |
|---|---|
| Unable to connect | Your host isn't reachable from the internet |
| 3xx redirect | Stripe counts any redirect as a failure; register the final URL instead |
| 4xx error | The server refused it, the URL is restricted or missing, or it doesn't accept POST |
| 5xx error | Your application errored; check its logs |
| TLS error | A certificate problem in your server's chain |
| Timed out | Your handler did slow work before replying |
You can see every attempt on the Event deliveries tab for the endpoint in Workbench, marked Delivered, Pending or Failed, with the status code and the time of the next retry.
Keeping your endpoint secure
Without verification, anyone could send fake events to fulfil orders or grant access. Stripe recommends two protections together: signature verification and allowing only Stripe's published IP addresses. Each endpoint has its own secret, and it's different for test and live keys on the same endpoint.
Want AI doing the heavy lifting in your marketing?
I build the systems that handle the boring 80 percent, so you get your week back. Done properly, with the human kept in.
The signature includes a timestamp to block replay attacks, and Stripe's libraries reject events more than 5 minutes old by default. Never set that tolerance to 0, because that switches the recency check off entirely. When you roll a secret, you can delay the old one's expiry for up to 24 hours while you update your server.
My verdict on Stripe webhooks
I'd set up webhooks before you rely on Stripe for anything that runs on its own, like subscriptions or fulfilment, because a redirect page can't tell you about a renewal or a dispute. Subscribe to a handful of events, answer fast, queue the rest, and store event IDs. If you're on recurring plans, read how Stripe Billing handles subscriptions alongside this, and if you're using Stripe's hosted checkout page, webhooks are how your system hears that a payment completed.
A webhook is the moment your payment system tells the rest of your business what just happened. Wiring that into a team's daily workflow is what I do. I run AI training and workshops for teams, and help businesses with AI enablement, AI automation and workflow set-up, so the tools you already pay for do more of the work. Most of that work is with SaaS marketing teams and small businesses. Here's how we can work together.
Frequently asked questions
How long does Stripe retry a failed webhook?
In live mode, up to three days with exponential back-off. In a sandbox, three times over a few hours.
Why does Stripe webhook signature verification fail?
Usually because the framework changed the raw request body. Stripe needs the unmodified raw body, the Stripe-Signature header and the right whsec_ secret.
Does Stripe send webhook events in order?
No. Stripe doesn't guarantee delivery order, so don't make your endpoint depend on it. Track event IDs instead.
Can I test Stripe webhooks without a public URL?
Yes. Use the Stripe CLI to forward events to your local endpoint with stripe listen, or a tunnelling tool such as ngrok.
How many webhook endpoints does Stripe allow?
You can register up to 16 event destinations.


