What is a Stripe webhook?
A Stripe webhook is an HTTP POST request that Stripe sends to a URL you registered, telling your server that something happened in your Stripe account: a payment succeeded, a subscription renewed, a card expired, a customer disputed a charge. These events happen inside Stripe, so the only way it can tell your server is to send it a request carrying the event. You register the endpoint in the Stripe Dashboard or through the API and list the event types you want to receive.
How do I receive Stripe webhooks while developing on localhost?
Stripe can deliver requests only to public HTTPS URLs. A dev server on localhost is not reachable by Stripe, so nothing it sends arrives there. To make your localhost URL public, you need to install a tunnel, which is not a trivial task, or use the Stripe CLI listener.
A simpler option is to use a ReqBin webhook URL. Create one with the ReqBin Webhook Tester, enter that URL in the Stripe Dashboard as the endpoint, trigger the events you need in a Stripe sandbox, and read what arrived. On the inbox page you see the requests as they land.
Can I edit a caught request and send it to my own server?
Yes. On the inbox page, select any of the requests you received from Stripe and press Open in API Tester. The request opens in the ReqBin API tester with everything that arrived. Enter your URL, change what you need, and send the request to your server. To send requests to localhost or to another server on your local network, you need to add the ReqBin Google Chrome Extension to your browser using this link.
What does a Stripe webhook payload look like?
Every Stripe event has the same envelope — id, type, created and the resource itself under data.object.
Two fields decide how you read the payload. type says what happened, payment_intent.succeeded in this example, and it is what your handler branches on. data.object is the resource, and its shape follows that type: a payment_intent here, an invoice or a customer for other events. The full list of types is in the Stripe API reference.
What headers does Stripe add to the request?
Two, on top of the ones any POST carries:
| Header | Value |
|---|---|
| Stripe-Signature | t=1757425320,v1=5f2c…,v0=6ffb… |
| User-Agent | Stripe/1.0 (+https://stripe.com/docs/webhooks) |
How does Stripe sign a webhook?
Your production endpoint URL is public, so anyone who knows or guesses it can post to it. Stripe signs every request it sends, and your handler checks that signature before it trusts the payload. The signature arrives in Stripe-Signature, a comma-separated list of prefixed values. t is the timestamp of this delivery attempt. v1 is an HMAC-SHA256 over the timestamp, a dot and the raw request body, keyed with the endpoint's signing secret (whsec_…).
Does Stripe retry a failed webhook?
Yes. An endpoint that does not answer 2xx gets the event again. A redirect counts as a failure too: Stripe does not follow 3xx, so an endpoint that redirects never received anything. In live mode the retries run with exponential backoff for up to three days; in a sandbox, three attempts over a few hours.
A retry is not a new event. id and created stay the ones from the first attempt, and only t in the signature changes, because Stripe signs each attempt as it is sent. So deduplicate on id and never on created: events are not delivered in the order they were generated, and two of them can share a second. An endpoint that decides by the payload contents provisions twice on the first retry it receives.