POST, and you can also read it from GET /v1/events for 30 days. Events cover orders today.
The format follows Standard Webhooks, so any Standard Webhooks library can verify our signatures.
Register your endpoint
We register your endpoint for you. There’s no API for it. Send us the URL, and we send you its signing secret. The URL must meet these rules:
We check the address each time we connect, after the host name resolves. We don’t use a proxy, and we don’t follow redirects.
You receive the events created after we register your endpoint. Events from before then are in
GET /v1/events. If you move to a new URL, we keep your secret, and events that are still due go to the new URL.
What we send
Each event is aPOST with a JSON body and these headers:
The body, formatted here for reading:
Event body
New event types can appear without notice. Ignore any type you don’t handle. See Versioning.
Verify each event
Check the signature before you trust an event. Anyone who learns your URL can send it a request.1
Read the raw body
Use the body bytes exactly as they arrived. Parsing the JSON and writing it out again changes the bytes, and the signature won’t match.
2
Check the timestamp
Reject the event if
webhook-timestamp is more than 5 minutes before or after your server’s clock. This is the default tolerance in the Standard Webhooks libraries, and it stops an old event from being replayed. We stamp each attempt when we send it, so a retry carries a fresh timestamp.3
Build the signed content
Join the
webhook-id header, the webhook-timestamp header and the raw body with a full stop between each: id.timestamp.body.4
Compute the signature
Your secret starts with
whsec_. Base64 decode the part after whsec_, and use the decoded bytes as the key. Don’t use the secret text itself. Compute an HMAC-SHA256 of the signed content with that key, and base64 encode the result.5
Compare
Split
webhook-signature on spaces. Accept the event if any entry is v1, followed by your value. Compare with a constant-time function, not ==.Test your verification
Run your code against this example before your first real event. The secret is made up for this page and signs nothing else. The body is on one line, exactly as we send it. Your code should accept it once you allow for the old timestamp.Test vector
Respond
Return any2xx status within 10 seconds. We read up to 64 KiB of your response and ignore what it says. Save the event and do the work after you respond, so a slow task doesn’t turn into a failed delivery.
Retries
We send each new event within about 5 seconds of recording the change. If an attempt fails, we try again on this schedule:
We stop sending an event 28 hours and 51 minutes after the change it describes. It stays in
GET /v1/events.
Delivery is at least once. You can receive the same event twice, and a retried event can arrive after a newer one. Use
webhook-id to ignore duplicates and version to ignore stale changes.
Catch up on missed events
GET /v1/events lists every event for 30 days, oldest first, whether or not your endpoint accepted it. Use it after downtime, or instead of a webhook endpoint.
- Call
GET /v1/eventswithout a cursor. Passlimitto set the page size, from 1 to 100. The default is 25. - Process the events, then store the
next_cursorfrom the response. - Next time, pass that value as
cursor. When there are no new events, you get an empty page and the same cursor back, so keep it for next time.
Rotate your secret
We rotate your secret when you ask. For 24 hours after a rotation, we sign every event with both the new and the old secret, sowebhook-signature carries two entries, the new one first. Switch your server to the new secret within those 24 hours. We can start another rotation only after the 24 hours end.