Webhooks to your own systems
Send orders, stock and product changes to your own systems as signed HTTPS posts, check the signature, and how retries, the delivery log and automatic turning off work.
Reference3 min read
A webhook sends the events you choose to an address of yours, such as a warehouse system or an ERP, as they happen. Webhooks are in the Growth and Enterprise plans, with the management API. Admins add them under Webhooks.
Add a webhook
- Open Webhooks and select New webhook.
- Enter the address, starting with
https://, such ashttps://erp.northwilds.com/hooks/commerce, and what it's for. - Choose the events, such as Order placed and Order paid.
- Select Save, and copy the signing secret. It starts with
whsec_and is shown only once. The key button makes a new one; the old one stops working at once.
Send a test posts a commerce.webhook.test event straight away and shows the answer. Each shop can have 10 webhooks, and only gets events from its own shop.
What arrives
A POST with a JSON body:
{ "id": "1f6c6a52-4f7e-4a63-9c39-5d8b2b6c0c11", "type": "commerce.order.paid", "createdAt": "2026-10-30T09:14:02.118Z", "organisationId": "6650c0ffee0000000000abcd", "subject": { "resourceType": "order", "id": "6650c0ffee0000000000f00d" }, "data": { "orderId": "6650c0ffee0000000000f00d", "reference": "NW1042", "status": "open", "paymentStatus": "paid", "fulfilment": "unfulfilled", "total": 46700, "currency": "EUR", "lines": 3, "source": "checkout" }}data is the same as the event's data in Events. Fetch anything more, such as the order's lines, from the API.
Orbit-Signaturet=<unix seconds>,v1=<signature>Orbit-Webhook-IdOrbit-EventUser-AgentCommerceOrbit-Webhooks/1| Header | What it holds |
|---|---|
Orbit-Signature | t=<unix seconds>,v1=<signature> |
Orbit-Webhook-Id | The delivery's id, the same on every retry. Use it to spot duplicates. |
Orbit-Event | The event type. |
User-Agent | CommerceOrbit-Webhooks/1 |
Answer with any 2xx status within 10 seconds. Do the work afterwards, not before you answer. Redirects aren't followed.
Check the signature
The signature is the hex HMAC SHA-256 of the time, a full stop and the raw body, with your signing secret as the key. Compare it in constant time, and refuse a time more than five minutes old.
import { createHmac, timingSafeEqual } from "node:crypto";export function verified(header: string, body: string, secret: string): boolean { const parts = Object.fromEntries(header.split(",").map((part) => part.split("=") as [string, string])); const time = Number(parts.t); if (!time || Math.abs(Date.now() / 1000 - time) > 300) return false; const expected = createHmac("sha256", secret).update(`${time}.${body}`).digest("hex"); return expected.length === parts.v1?.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));}Use the body exactly as it arrived, before parsing it.
Retries
A delivery that doesn't get a 2xx answer is tried again after 1 minute, then 5 and 30 minutes, then 2, 6, 12 and 24 hours, 8 tries in all. Recent deliveries under each webhook shows every delivery with its answer and time, and Send again for one that failed. Deliveries are kept for 30 days.
A webhook that has failed at least 10 times over 3 days is turned off, and Admins get a notification in Orbit. Fix your system, then turn it on again. Events while it was off aren't sent later.
Addresses
Webhooks only go to https addresses on the internet. Addresses that lead to a private network, such as 10.0.0.5 or a name that resolves to one, are refused.
Plans
On a plan without the management API, webhooks stop and no new events are queued, and nothing is lost from your settings. See Plans.
Was this page helpful?