# Webhooks

Receive provider events with exact-body signature verification, idempotency, route limits, and durable processing.

Ferrite webhooks keep the original request body and selected signature headers
intact so your app can authenticate the provider before parsing or applying an
event.

## Declare only the required header

```json filename="ferrite.json"
{
  "services": [{ "name": "shop-requests", "primitive": "queue" }],
  "applications": [{
    "name": "shop",
    "artifact_key": "apps/shop@v1",
    "artifact_sha256": "0000000000000000000000000000000000000000000000000000000000000000",
    "bindings": [
      { "handle": "requests", "kind": "queue", "service": "shop-requests" }
    ],
    "routes": [{
      "prefix": "/api/stripe/webhook",
      "binding": "requests",
      "kind": "api",
      "forward_headers": ["stripe-signature"],
      "rate_limit_per_min": 600
    }]
  }]
}
```

Verify the signature over the exact bytes, reject timestamps outside the
provider window, and store the provider event ID before applying a domain
transition. Return success for an already-processed event.

## Process after verification

The shop uses a signed Stripe webhook to reconcile payment completion. The
handler validates the signature, checks order and amount, records the event
idempotently, updates payment state, and queues downstream email and
fulfillment work. The browser redirect is never treated as proof of payment.

## Protect the route

Apply a route-specific rate limit and keep the handler’s work bounded. Do not
log the full request body or signature. Store provider secrets with
[Secrets](/developers/secrets-egress/) and send provider API calls only through
the declared egress allowlist.

See the [Shop app](/developers/apps/) for a complete payment flow.
