Skip to content

Outbound webhooks

Beluga can POST to your own endpoints when something happens in the store. Wire up a fulfilment provider, an accounting ledger, a Slack channel, a Zapier-style connector, or internal alerting, without either side shipping code into the other’s process. There is no SDK, no app to register, nothing of yours running inside Beluga. If you can serve an HTTPS endpoint, you can integrate.

Event Fires when data
order.paid The Stripe webhook confirmed payment. Not a success page: the money is real. Order
order.updated Fulfilment status, carrier or tracking number changed Order
order.refunded A refund settled, partial or full. Compare refundedCents with totalCents. Order
order.cancelled An order was cancelled from the admin. Not also order.updated. Order
product.published A product was published to Stripe. Carries kind. Product
inventory.low A sale left a finite variant at five or fewer. Per variant, per sale, not latched. Variant

Admin → Webhooks → Add an endpoint. A URL, the events you want, save.

You are shown the signing secret exactly once, on that screen. Copy it into your receiver’s environment there and then. Nothing reads it back. If you lose it, Roll secret mints a new one, and the old one stops working the moment you roll, so expect failed deliveries until the receiver has the new one.

The URL must be https:// and its hostname must resolve to a public address, checked at creation and again before every send, redirects included. Without that, an endpoint pointed at 169.254.169.254 would turn admin access into a way to read the host’s cloud metadata out of the delivery log. For a receiver that genuinely lives on localhost, WEBHOOK_ALLOW_INSECURE_TARGETS=true lifts both rules; leave it off anywhere public.

Nothing is sent from a request handler. Events are queued and delivered by a background pass every ten seconds, which keeps a slow subscriber from delaying Beluga’s response to Stripe; a delay there would trip Stripe’s own retry and re-enter the payment handler.

Delivery is at-least-once, retried on failure at 1 minute, 5, 25, 2 hours and 10 hours, then given up. An endpoint whose deliveries have given up five times running is switched off, and the admin says so; re-enabling clears the count and sends what is still queued. The endpoint’s row expands into its recent deliveries, with Redeliver on each.

An envelope, { id, type, created, data }, with the id repeated in a beluga-event-id header and a beluga-signature header of t=<unix seconds>,v1=<hmac> over ${t}.${rawBody}. The same shape as Stripe’s, so if you already verify Stripe’s webhooks this is that code with a different header name.

Everything a receiver needs, with payload examples per event, verification in Node and Python, the raw-body trap, local testing and a troubleshooting table, is on Build a receiver. Connect a fulfilment webhook is the tutorial.