Provider guide

Stripe webhooks, kept properly.

Stripe's webhooks are the good kind, signed, retried, documented. They still go missing: endpoints get disabled after failures, events expire, secrets rotate. Here's how Stripe behaves, and how a vault under it turns "what happened?" into a search box.

Field notes

How Stripe behaves, and where it bites

Signed, retried, documented; and still able to go quiet without a single error. The essentials before you wire anything.

How Stripe sends

Stripe POSTs JSON events (invoice.payment_succeeded, charge.refunded, checkout.session.completed…) to endpoints you register, signs each request with Stripe-Signature, and expects a 2xx quickly. On failure it retries with backoff for up to 3 days in live mode, then that delivery is gone. Repeated failures can disable the endpoint entirely, and Stripe tells you by email, easy to miss in a busy inbox.

Where it bites

  • Event payloads are frozen at your account's API version. Testing an old integration against current payloads, or vice versa, needs the actual historical bodies. If you didn't keep them, nobody did.
  • The 3-day retry window is shorter than your incident. A bad deploy on Friday plus a long weekend can outlive Stripe's patience.
  • Disabled endpoints fail silently forward. Everything after the disable simply never arrives, the classic absence failure.

The vault under it

Point a WebhookVault endpoint at Stripe (or fan Stripe's deliveries through it to your app with forwarding), and every event is stored in full for up to a year: signature headers verbatim, bodies byte-for-byte, searchable by the event type or, on Pro, by anything inside the payload. Replay any event to your endpoint after you fix the bug; bulk-replay the whole outage window through the rate-smoothed pipeline.

01

Create an endpoint

Name it stripe-production. Turn on forwarding to your app's handler if you want the relay pattern.

02

Register it with Stripe

Dashboard → Developers → Webhooks → add the capture URL. Send a test event; watch it land live in the explorer.

03

Keep everything

Every invoice.paid from here on is on the shelf, searchable, replayable, and ready for the Watchdog that notices the day it stops.

Verify signatures from the record

The vault stores Stripe-Signature with every event, so you can verify any historical delivery: HMAC-SHA256 your signing secret over "{timestamp}.{raw body}" and compare to v1. Preset verification with per-request verdicts: the Stripe preset (Solo and up) gives every row its verdict.

captured · charge.refunded147 B · kept
{
  "id": "evt_3RxKsNkdIwHu7ixC",
  "type": "charge.refunded",
  "data": { "object": {
    "id": "ch_3RxKsN",
    "amount_refunded": 2900,
    "refunded": true
  } }
}

Event catalogue

Stripe webhook events you'll see

A few of the events Stripe sends, not the full list: Stripe's own documentation has every one. Each arrives in the vault labelled with its event type.

EventWhat it tells you
checkout.session.completedA customer finished Checkout: paid, or saved a payment method for later.
payment_intent.succeededA payment went through.
invoice.paidAn invoice was paid, subscription renewals included.
invoice.payment_failedA renewal or invoice charge failed, and Stripe's retry schedule for the card begins.
customer.subscription.updatedA subscription changed: plan, quantity, status or a scheduled cancellation.
customer.subscription.deletedA subscription ended.

Questions

Stripe webhook questions, answered

The three that come up most when Stripe is wired to a new endpoint.

How long does Stripe retry a failed webhook?

In live mode Stripe retries with exponential backoff for up to three days; in test mode the retries stop within a few hours. After that the event is not sent again. With a WebhookVault endpoint in front of your handler, the vault answers Stripe as soon as the event is stored, its own outbox retries your handler, and once you have fixed the bug you can replay the event from the record.

Why did Stripe disable my webhook endpoint?

Stripe can disable an endpoint that has kept failing over several days, and it warns the account by email first. After that nothing is sent, so nothing errors either: the failure looks like silence. Re-enable the endpoint in the Stripe Dashboard once it answers 2xx again. A Watchdog on the endpoint (Solo and up) raises the alarm when an expected event stops arriving.

How do I verify the Stripe-Signature header?

The header carries a timestamp t and one or more v1 signatures. Compute HMAC-SHA256 over {t}.{raw body} with the endpoint's signing secret, compare it to each v1 value in constant time, and refuse a timestamp outside your tolerance. Use the raw bytes, never re-serialised JSON. On Solo and up the vault does this at capture and puts the verdict on every row.

Give Stripe a vault to land in.

Free tier keeps 3 days of events while you evaluate; Solo keeps a month from $5.