Once an endpoint is registered it starts receiving calls from us as soon as an order closes. What those calls look like, how to verify their signature, and what your endpoint has to answer are all on The callback we send you.
Configure webhooks
/api/v1/webhookorders:writeSet, change, or disable the URL we notify when an order reaches a final state · This page covers registering the endpoint only; what the call we send you looks like, how to verify its signature, and how retries work are on the callback page (/developers/webhooks/callback) · An endpoint belongs to one API key, not to your account, so if you use several keys you register one endpoint per key
The signing key is shown once, and rotates on every successful registration
secret is shown exactly once, in the answer to this request. No endpoint can read it back. Store it in your secret store the moment you receive it; if you lose it, the only way forward is to register again and receive a new one · Every successful registration rotates the key, even if you send the very same URL. The previous key stops working at that instant — so do not replay this request to confirm your settings, or your verifier will start rejecting every payload while it still holds the old key · Disabling the webhook clears the key at the same moment, and enabling it again always issues a new one.
Register your endpoint before you start ordering
If you register a URL after placing orders, you will not be told about the ones already in flight — events queued while no endpoint was configured are dropped on the next round and are never replayed once you do register, and there is no way to ask for them again · Disabling the webhook while orders are still open has the same effect · Register your endpoint before you start ordering, and if you only registered later, sweep your own unfinished orders once.
The authentication refusals reach this endpoint too
The five refusals at the authentication gate — invalid_key (401) · key_revoked (401) · insufficient_scope (403) · quota_exceeded (429) · too_many_inflight (429) — can happen on every endpoint, this one included, because they are thrown before the request reaches a single line of this endpoint's own logic (the scope it needs is the one declared at the top of this page). The response tabs above therefore list only what this endpoint itself answers; that is not a claim that those five cannot happen here. All of the detail lives in one place, on the identity endpoint page: the HTTP status of each one, which of them spend your daily quota, which headers come back, and why the two 429s need opposite handling. It is deliberately not copied onto every page, because the second copy is the one nobody remembers to update the day the rules change.
Request parameters
Request body
| Name | Type | How to use it |
|---|---|---|
| urlRequired | string or null | The URL we should call, or null to disable the webhookDetails and caveats
|
Header
| Name | Type | How to use it |
|---|---|---|
| AuthorizationRequired | string | Your key as Bearer <key> — required on every request · The endpoint you register belongs to this key alone. |
| Content-TypeRequired | string | Must be application/json because this request carries a bodyDetails and caveats
|
Response fields
| Name | Type | How to use it |
|---|---|---|
| url | string or null | The URL we actually stored and will call Details and caveats
|
| secret | string | The secret you verify the signature of our calls with (how to use it is on the callback page) Details and caveats
|
| rotated_at | ISO 8601 string (UTC) | When this signing key was issued · Present only in the answer to registering or changing an endpoint, for the same reason. |
| data.reason | string | The only field that tells you which check the destination failed Details and caveats
|
Possible statuses
| Status | Code | Meaning |
|---|---|---|
| 200 | — | Endpoint registered or changed — store the secret nowThose three keys are the whole response Details and caveats
|
| 200 | — | Webhook disabled — no more notifications Disable it by sending url as nullDetails and caveats
|
| 400 | bad_request | Body is not a JSON object An array, a bare string, a number or an empty body · Every check on this page runs in the order shown, because the first check that fails is the one you get back. |
| 400 | bad_request | No url key in the bodySent {}, {"webhook_url": "https://hooks.example.com/naxset"}, or {"URL": "https://hooks.example.com/naxset"}Details and caveats
|
| 400 | bad_request | url longer than 2048 charactersChecked before the URL rules, because it is a complaint about the shape of your request rather than about the endpoint being unsafe Details and caveats
|
| 400 | webhook_url_rejected | malformed — not a URL at allSent {"url": "not a url"}, {"url": ""}, or a value that is not a string at all such as {"url": 123} (objects and true included)Details and caveats
|
| 400 | webhook_url_rejected | not_https — wrong schemeSent http://hooks.example.com/naxset · Anything that does not start with https:// is refused, with no exception for internal networks or for testing. |
| 400 | webhook_url_rejected | bad_port — a port other than 443Sent https://hooks.example.com:8443/naxset · Writing :443 explicitly is fine; anything else is not. |
| 400 | webhook_url_rejected | ip_literal — the host is an address, not a nameSent https://203.0.113.10/hookDetails and caveats
|
| 400 | webhook_url_rejected | own_domain — pointing back at the shop, or at a domain the shop itself runs onThis check covers three domains, not just the shop's own: naxset.com, any host under vercel.app, and localhostDetails and caveats
|
| 400 | webhook_url_rejected | private_host — the name resolves somewhere privateThe hostname is a real name, but at least one of the addresses it resolves to is in a private, loopback, link-local or otherwise reserved range Details and caveats
|
| 400 | webhook_url_rejected | unresolvable — we could not get a usable answerThe name does not resolve, the lookup failed, or an address came back that we could not read Details and caveats
|