Webhooks
Register a URL to be called the moment something happens, and know the three things that will surprise you.
Manage → Settings → Webhooks.
"Register a URL to receive events as they happen, instead of polling the API."
This is why a store is reachable through the API at all rather than through a polling loop. An agency running fifty stores registers fifty times with fifty calls, instead of clicking through fifty settings screens.
Adding an endpoint
Add endpoint.
Endpoint URL — your system's address, e.g.
https://example.com/hooks/web-builder.
Description — an internal label, e.g. "Order sync to the warehouse system".
Events — at least one. There are eight:
| Event | Fires when |
|---|---|
order.created |
An order is placed |
order.updated |
An order's status, payment or fulfillment changes |
customer.created |
A customer is added |
customer.updated |
A customer record changes |
product.created |
A product is created |
product.updated |
A product changes |
product.deleted |
A product is removed |
page.published |
A page goes live |
Enabled — the switch.
An endpoint subscribed to nothing would never fire, with no error anywhere to notice by — which is why the form requires at least one.
Three things that will surprise you
1. The signing secret is shown exactly once
"The signing secret is only shown after you save." And only then.
There is no route that reads it back — GET, list and update all return the endpoint with no secret field at all, not even an empty one. Lose it and the fix is to delete the endpoint and register a new one.
Why so strict: webhooks.read is grantable on its own and reaches the lowest
role on a store. A GET that could return a signing secret would let that role
forge deliveries into your own system.
2. Duplicates will happen
Delivery is at-least-once, never exactly-once. A delivery the platform believes failed — a timeout, a dropped connection, a 5xx — is retried in full, even when your system actually processed it and only the response back was lost.
If your receiver turns order.created straight into a charge, an email, or a
stock decrement without checking anything first, it will eventually do it twice.
The defence: every delivery carries an id that is stable across every
attempt (also the X-WB-Event-Id header). Record the ones already handled,
and no-op a repeat.
3. There is no ordering
Deliveries go out in the order they become due, not the order the events happened in. A retry sits behind its backoff, so a fresher event for the same resource can arrive first.
Do not infer sequence from arrival order. If you need one, read the resource's
own updatedAt.
Status
| Status | Means |
|---|---|
| Active | Normal |
| Disabled | You switched it off |
| Failing | Three consecutive delivery failures |
Failing does not mean dead: "Still retrying — one success clears this."
The retry schedule is seven attempts spanning about 32.6 hours: immediately, +1 minute, +5 minutes, +30 minutes, +2 hours, +6 hours, +24 hours. Long enough to outlast a deploy, an expired certificate or a night's outage; short enough that nothing is still being retried next week.
An endpoint is not disabled by this — it keeps receiving new events, because an integration that comes back on its own should catch up by itself rather than be switched off silently.
Checking whether it works
Send test event fires a real delivery at your URL.
View deliveries shows recent attempts with their status codes and errors. The Last success column in the list answers "is it alive" at a glance.
Note: the delivery history never includes the payload. If you need to know exactly what was sent, the envelope your receiver already got is it.
Blocked URLs
A URL pointing at a private, loopback or link-local address is refused
immediately with 400 blocked_url. The address is checked again at dial
time, because a hostname's DNS answer can change after the first check — and
redirects are never followed.
There is nothing to debug: the answer comes back at once.
Read-only
If your role only has webhooks.read, the screen says so: "You can view
registered endpoints, but not create or change them."
Verifying signatures
Every delivery carries an X-WB-Signature header. How to check it — including
the detail people get wrong (the signed string uses Unix seconds, not the
RFC3339 value in the header) — is in The public
API.
Updated 22/08/2026