# Receive Stripe webhooks

Point a Stripe webhook endpoint at Webhooker, verify the Stripe signature with your whsec_ signing secret, and forward events to your app with retries.

Source: https://docs.webhooker.eu/tutorials/stripe/

## Before you start

- A Stripe account with access to **Developers → Webhooks**.
- An endpoint of your own that can accept a `POST`, or nothing at all if you
  only want to inspect events for now.

## 1. Create the source

In Webhooker, open **Sources → Create source**, name it `stripe-prod` (or
`stripe-test`), and copy the ingest URL:

```
https://app.webhooker.eu/in/6b225n04u5kmyg
```

## 2. Add the endpoint in Stripe

Stripe Dashboard → **Developers → Webhooks → Add endpoint**:

- **Endpoint URL** — the ingest URL.
- **Events to send** — pick the ones you actually handle. `invoice.paid`,
  `checkout.session.completed`, `customer.subscription.updated` are common
  starting points. "Send all events" works too and is easy to filter later.

Save, then reveal the endpoint's **Signing secret** — it starts with `whsec_`.
Test mode and live mode have separate secrets, and
[where each `whsec_` value comes from](https://webhooker.eu/blog/stripe-webhook-signing-secret)
explains which one you are looking at.

## 3. Turn on verification

Back in Webhooker: the source's **Settings** tab → **Authenticate inbound
requests** → method **Stripe** → paste the `whsec_…` secret → save.

Stripe signs `{timestamp}.{body}` and Webhooker checks it with a 5-minute
tolerance, so replayed requests are rejected. The scheme is written out in
[verifying Stripe webhook signatures](https://webhooker.eu/blog/verify-stripe-webhook-signatures),
and the wider
[Stripe webhooks guide](https://webhooker.eu/blog/stripe-webhooks-guide) covers
which events to subscribe to and how resends behave.

## 4. Send a test event

In Stripe, use **Send test webhook** on the endpoint. Within a second it appears
in the source's **Overview** live tail with `verified`.

If it says `failed`, the secret does not match — copy it again, in full. Test
mode and live mode have different secrets, and so does every endpoint.

## 5. Forward it to your app

Open the **Destinations** tab → **Add destination** → **Create new**:

- **Name** — `billing-api`.
- **URL** — your handler, for example `https://api.acme.dev/hooks/stripe`.
- **Response timeout** — 10 seconds is plenty; answer fast and queue the work.
- **Sign outbound requests** — HMAC signature, with a long random secret you
  keep.

Save. The next Stripe event is forwarded, and the event's delivery row shows
what your app answered. Verification code for the signature is in
[verify our signature](/deliver/verify-signatures/).

## 6. Filter, if you took "all events"

Stripe is chatty. To keep one gateway focused, open **Edit routing** on the
destination row and add a filter:

```json
{
  "operator": "or",
  "rules": [
    { "path": "body.type", "op": "eq", "value": "invoice.paid" },
    { "path": "body.type", "op": "eq", "value": "invoice.payment_failed" }
  ]
}
```

Everything else is recorded as `filtered` — visible in the log, not sent.

## Verify it works

1. Stripe shows `200` for the endpoint.
2. The Webhooker event is `verified`.
3. The delivery is `succeeded`.

All three green means the chain is complete.

## Troubleshooting

| Symptom | Cause |
| --- | --- |
| Stripe shows `401` | Wrong signing secret in Webhooker, or the wrong mode's secret. |
| Stripe shows `429` | Over the source's ingest budget. See [limits](/reference/limits/). |
| Events `verified`, delivery `failed` | Your endpoint answered a `4xx`. Check the attempt's response body. |
| Signature check fails in your handler | You are verifying Webhooker's signature with Stripe's secret. They are separate: the outbound secret is the one you set on the destination. |
| Duplicate processing | Stripe can send an event more than once, and delivery is at-least-once. Key on `X-Webhooker-Event-Id`. |
