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
{
"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 and send provider API calls only through the declared egress allowlist.
See the Shop app for a complete payment flow.