Skip to main content
Some things happen in a workspace on their own — an order gets paid, a customer places an order — with no one clicking anything in your app. Webhooks are how the platform tells your app about those events, by POSTing to a URL you own. Because that URL is public, every delivery is signed, so your app can be sure the event really came from Sentralbee and wasn’t forged or replayed by someone who found your endpoint. This page is about receiving platform events inside an app — the app declares the events it wants, and the platform wires the subscription up automatically on install. That’s different from the raw-API webhook story, where you manage subscriptions yourself with an API key; for that, see the Webhooks guide and the webhooks API reference. Same signing scheme, different setup.

1. Declare the events in your manifest

You don’t create a subscription with an API call. You just say, in your manifest, which events you want and where to receive them:
path is a route on your app; the full delivery URL is your public app URL plus that path. See The manifest for the full field list.

2. Get the signing secret on install

When a merchant installs your app, the platform creates the webhook subscription for you — pointing at <your public URL> + webhooks.path — and hands you the subscription’s signing secret the same way it hands you your API key: in the one-time provision call.
The merchant wires nothing. Store webhookSecret encrypted (the SDK’s createCypher is for exactly this), right alongside the API key. You’ll need it to verify every delivery. See Install lifecycle for the provision handler in full.

3. Verify every delivery

Sentralbee signs deliveries Standard-Webhooks (Svix) style. Each POST carries three headers: The signature is HMAC-SHA256(secret, "{id}.{timestamp}.{body}") over the raw request body. Two rules matter here, and the SDK enforces both for you:
  • Verify over the raw body, before you parse it. JSON.parse and re-serialize would change the bytes and break the signature, so read the raw text and verify that.
  • Fail closed. If anything is missing or doesn’t match, reject the request — don’t fall through and process it anyway.
verifyWebhookSignature(rawBody, headers, secret, options?) returns a boolean and never throws. It returns false — meaning reject — when the secret or any header is missing, when no signature matches, or when the timestamp is outside tolerance. The comparison is constant-time.
The timestamp check is a replay guard: even a validly signed payload is rejected if it’s more than toleranceSeconds old, so an attacker can’t capture one delivery and re-send it later. The default is 300 seconds. Set toleranceSeconds: 0 only if you have a reason to disable it.
The Webhook-Signature header can list several signatures separated by spaces. That happens during a secret rotation, when both the old and new secrets are briefly valid. verifyWebhookSignature accepts the delivery if any of them matches, so a rotation never drops events — you don’t have to handle this yourself.

Test it locally

The CLI signs sample events with the exact same scheme the platform uses, so your verification path runs for real without any Sentralbee infrastructure:
Point it at your running app (bun run dev) and watch your handler fire. If verification fails, you’ll get your own 401 back — which is the loop you want to test.

Where to next