Webhooks
Webhooks are fast hints, not the record of truth. The contract is deliberately boring: hints arrive fast, truth is pullable, and nothing depends on your uptime. Design accordingly, on either side of the marketplace.
- Every event shares one envelope:
{event_type, order_id, external_order_id, seq, status, substatus, units?, artifact?, error?, occurred_at}, with a per-order, strictly increasingseq. Retries reuse the sameseq. See the event envelope. - Both directions. Buyers receive order lifecycle events; sellers receive
order.needs_review,order.canceled,negotiation.offer, andcatalog.integrity_failed. Same envelope, same rules. See the event types. - Verification: every delivery is HMAC-signed over (timestamp, body) in
X-Signature; verify before trusting, reject stale timestamps. - Delivery is best-effort: 3 attempts with exponential backoff, roughly 25 seconds worst case, then log-and-drop. An outage on your side longer than that misses events, by design; recovery is built in, below.
- No public endpoint? Use a queue. Switchboard can deliver events to a shared SQS queue with AWS-native access control, first-class alongside webhooks. Timestamps are UTC everywhere.
The receiver, correctly
Four duties, in order: verify the signature, acknowledge with a 2xx fast, process async, and dedup on (order_id, seq). Return 204 for duplicates so retries stop.
app.post("/webhooks/switchboard", (req, res) => {
verifyHmac(req.headers["x-signature"], req.body); // reject stale timestamps
const { order_id, seq } = req.body;
if (seen(order_id, seq)) return res.status(204).end(); // dedup: retries reuse seq
enqueue(req.body); // ack fast, process async
res.status(200).end();
});
Recovery, always available
- Missed a window?
GET /v1/events?order={id}&after_seq={n}replays everything after your high-water mark. - Not sure where you are?
GET /v1/orders/{id}returns the snapshot withlast_seq; reconcile against it, then replay the gap. - After every deploy or outage: sweep
GET /v1/orders?status=submitted,validating,needs_confirmation,confirmed,negotiating,seller_reviewand replay each. Every non-terminal status is in that list; dropneeds_confirmationonly if you never submit document orders. - Long pauses are normal: orders park in
seller_reviewfor minutes or hours with no events. A low-frequency safety poll on parked orders costs nothing and closes the last gap.
The sweep is one half of the reconciliation posture; the rest is Reconciliation.
If you cannot expose a public endpoint at all, request shared-queue delivery (SQS) instead: same envelope, same seq semantics, no inbound firewall conversation.