Stripe Webhook Example

Inspect a Stripe webhook request as it arrives at your endpoint. See the event JSON and the headers Stripe sends with it. Find out how a delivery is signed and what changes when Stripe retries one. Catch a real Stripe webhook on a URL of your own with the ReqBin Webhook Tester. No sign-up, nothing to install.

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.

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. It is public, it takes whatever Stripe posts to it, and it shows you each request, as soon as it lands.

How do I connect a ReqBin webhook URL to Stripe?

  1. Create a webhook URL with the ReqBin Webhook Tester and copy it.
  2. In the Stripe Dashboard, register that URL as an endpoint:
    1. Open the Webhooks tab in Workbench and click Create an event destination.
    2. Select Your account, pick the API version and the event types you want to receive, and click Continue.
    3. Select Webhook endpoint as the destination type and click Continue.
    4. Paste the ReqBin URL into Endpoint URL.
  3. Place your test order and read the Stripe webhook details on your inbox page.

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.

Stripe Webhook Example
{
  "id": "evt_1P9x2K2eZvKYlo2C0KzQq8rM",
  "object": "event",
  "api_version": "2026-08-27",
  "created": 1757425320,
  "type": "payment_intent.succeeded",
  "livemode": false,
  "data": {
    "object": {
      "id": "pi_3P9x2K2eZvKYlo2C1aBcDeFg",
      "object": "payment_intent",
      "amount": 4900,
      "currency": "usd",
      "status": "succeeded",
      "customer": "cus_QK8vN2mLpXyZ01",
      "description": "Annual plan"
    }
  },
  "request": {
    "id": null,
    "idempotency_key": null
  }
}

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:

HeaderValue
Stripe-Signaturet=1757425320,v1=5f2c…,v0=6ffb…
User-AgentStripe/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 delivery, and you need to verify the signature before trusting the request. 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, with exponential backoff for up to three days in live mode and three attempts over a few hours in a sandbox. A redirect counts as a failure too: Stripe does not follow 3xx. A retry carries the same id under a new signature, so deduplicate on id and never on created.

Updated: