POST when something happens on your account. What each event carries: Webhook events.
Setup
We set the webhook up for you. Contact us with your key’s name and your URL, and we give you a signing secret, once. It startswhsec_. Keep it in a secret manager, like your API key.
- The URL has to be
httpson port 443, on a public hostname: no IP addresses and no private or internal addresses. We check it when we set it up and again on every delivery. - The key has to be linked to an account and hold
read:account, because events carry what your order history shows.shipment.updatedalso needsship:cards. - One webhook per key. Events start when the webhook is set up, and nothing earlier is replayed.
- Pausing holds events, and turning it back on sends what happened meanwhile. Disabling the key does the same.
- A new secret replaces the old one at once, with no overlap. Deliveries fail your check until you install it, and retry until then.
- Linking the key to another account stops the webhook until we remove it and set it up again, so one account’s events never reach another’s URL.
What you receive
We look for new events every 2 minutes, or up to 18 minutes apart at our busy times, and longer while a webhook is paused.
Answer quickly
Return any2xx within 5 seconds, and do slow work after you’ve answered. Anything else counts as a failure, including a redirect, which we don’t follow. We never read the response body.
Check the signature
Every request carriesX-CatchBack-Signature: t=<unix seconds>,v1=<hex>. v1 is the HMAC-SHA256 of <t>.<raw request body>, keyed with your whole secret, whsec_ included.
- Take the raw bytes of the body, from before any JSON parsing. A framework that parses JSON for you throws them away, so read the body first.
- Compute the HMAC and compare it to
v1in constant time. - Refuse a
tmore than 5 minutes from your clock. This stops a replay. Each retry carries a newt, so a retry never ages out.
raw_body is bytes.
Test your code
Run your check against this vector, with your clock check off or with your clock set tot (the functions above take the current time as their last argument). It should pass, and fail if you change one character of the body.
Retries
A failure is retried on this schedule, counted from the failure before it:
These are the shortest waits: retries go out when we next look for events. That’s about 45 hours in all. After the eighth attempt the event stops and shows as failed on our side. Contact us and we can send failed events again.
If your endpoint is down, answers
429 or a 5xx, or doesn’t answer, we stop sending that run’s other events, so one outage doesn’t use up the attempts on everything queued behind it.
List orders always has every finished order and List shipments the current statuses, so reconcile against them after an outage.
Duplicates and order
Delivery is at-least-once and unordered. The same event can arrive twice, and a later event can arrive first.- Dedupe on
id. It never changes for an event, including across retries. - Don’t rely on order. For two events about one shipment, the later
status_updated_atis the newer.

