Webhooks
Receive signed order updates on your server.
Webhook setup and payloads
- Open Dashboard → 2. Order webhooks.
- Select Test or Live. Enter an HTTPS endpoint on the exact origin of your registered website, for example
https://shop.example/webhooks/tcgplaytest. - Select Save webhook and store the displayed
whsec_signing secret on your server. It is shown once. Saving again rotates that mode's secret; update your receiver.
Test and live settings are separate. Test carts can produce cart.expired after expiry and worker processing. Hosted and Connect simulator carts emit order.paid on completion. The test events API can then emit order.in_production, order.shipped or order.cancelled. These events include data.mode: "test" and data.simulated: true and go only to the Test webhook. They never start real fulfillment. Prepaid sandbox events retain their existing flow.
Live order events are order.paid, order.in_production, order.shipped and order.cancelled. Their envelope is {id, type, created_at, data}. Deduplicate by the top-level id; data.id is the order ID. Each lifecycle transition has its own event ID; retries preserve it.
{
"id": "evt_cccccccccccccccccccccccccccccccc",
"type": "order.shipped",
"created_at": "2026-10-08T15:00:00.000Z",
"data": {
"id": "00000000-0000-4000-a000-000000000042",
"cart_id": "cart_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"external_ref": "store-order-1042",
"status": "shipped",
"shipment": {
"carrier": "Example carrier",
"tracking_number": "TRACK-1042",
"tracking_url": "https://shipping.example/track/TRACK-1042"
}
}
}All order.* events use these data fields: id, cart_id, nullable external_ref, status and nullable shipment. Shipment has the same shape as in the order response. For cart.expired, data.id is the cart ID; there is no cart_id, shipment or order ID field:
{
"id": "evt_dddddddddddddddddddddddddddddddd",
"type": "cart.expired",
"created_at": "2026-10-13T12:00:01.000Z",
"data": {
"id": "cart_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"external_ref": "store-order-1042",
"status": "expired"
}
}created_at is an RFC 3339 timestamp with timezone. The envelope does not contain a mode field; select your test/live verifier using the webhook endpoint and its signing secret. No cart.open, cart.failed or order.blocked event is currently emitted; use cart polling and order reconciliation for those states.
Verify TCGP-Signature as t=unix_seconds,v1=hex_hmac: compute HMAC-SHA256 over the exact timestamp + "." + rawBody, using your signing secret. Do not parse and reserialize JSON before verification. Compare signatures in constant time and reject timestamps outside a five-minute (±300 seconds) tolerance.
After verification, durably store or enqueue the event and return any 2xx response within 15 seconds. Redirects are not followed. Failed deliveries retry up to 12 total attempts with increasing delays starting at 30 seconds and capped at one day; worker scheduling can delay attempts. Delivery is asynchronous, can repeat and has no ordering guarantee. Use order GET to reconcile the current state; contact support if deliveries are exhausted.