Skip to main content
Each time an order changes state, or more of it fills, we create an event. We send the event to your webhook endpoint as an HTTPS 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 a POST 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 any 2xx 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.
  1. Call GET /v1/events without a cursor. Pass limit to set the page size, from 1 to 100. The default is 25.
  2. Process the events, then store the next_cursor from the response.
  3. 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, so webhook-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.