Hold and book lifecycle
The end-to-end hold → book → release flow with contract-verified request and response JSON for every write and read endpoint.
Hold and book lifecycle
This guide walks the full server-side lifecycle of a seat: hold it,
book it, release it, and read its status or the parent
event at any point. Every JSON block below is copied verbatim from
the API contract (openapi.json) so what you read here is exactly what
the API sends and accepts.
This guide deepens the e-commerce recipe in Hold seats and confirm at checkout — the recipe shows the browser-to-cart-to-checkout choreography; this guide is the endpoint-by-endpoint contract reference. For the concepts behind the statuses, see Core concepts.
All write calls authenticate with a secret key
(X-Api-Key: sk_live_xxx); the read calls accept a public or secret key.
Two flows: which one are you building?
Who takes the initial hold depends on your integration flow. Get this
right or you will double-hold a seat (re-holding a seat the SDK already
holds returns a 409).
- SDK checkout — the browser SDK auto-holds each seat on select and
captures the
holdTokenclient-side. Your server calls onlyextend/book/release, and neverPOST /seats/hold. Every endpoint below still applies — just skip step 1 (hold), because the SDK already holds the seat. - Headless / server-authoritative — no JS SDK; your server owns the
whole lifecycle. It calls
hold(step 1) first, thenbook. The rest of this guide walks this flow end to end.
1. Hold a seat
This step runs only in the headless flow. In SDK checkout the SDK has already taken the hold — skip to step 2.
Take a hold on a single seat with POST /events/{eventKey}/seats/hold.
The holdToken you pass ties the hold to a buyer session; extraData is
an opaque bag you can round-trip (e.g. your order id). A successful hold
returns 201 with the seat's new state and its holdExpiresAt.
The initial hold's ttlSeconds defaults to 900s (15 min) when
omitted; the SDK forwards its holdDurationSeconds into this field — see
the holdDurationSeconds field in the SDK reference for the
full reconciliation.
POST /api/v1/events/evt_q3-2026-jazz-night/seats/hold HTTP/1.1
Host: seatbuilder.org
X-Api-Key: sk_live_xxx
Content-Type: application/json
{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","extraData":{"orderId":"ord_98a2"}}{"chartKey":"chart_8a2b1c","eventKey":"evt_q3-2026-jazz-night","holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","holdExpiresAt":"2026-10-02T18:55:00.000Z","objects":[{"objectLabel":"A-12","status":"held","extraData":{"orderId":"ord_98a2"}},{"objectLabel":"A-13","status":"held","extraData":{"orderId":"ord_98a2"}}]}2. Book the held seats
At checkout, promote the holds to permanent bookings with a single
all-or-nothing call to POST /events/{eventKey}/seats/book. Pass every
seat label in objectLabels[] and the shared holdToken; all the labels
must currently be held by that token. Success returns 200 with a
booked[] array — one object per confirmed seat.
POST /api/v1/events/evt_q3-2026-jazz-night/seats/book HTTP/1.1
Host: seatbuilder.org
X-Api-Key: sk_live_xxx
Content-Type: application/json
{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","extraData":{"orderId":"ord_98a2"}}{"booked":[{"chartKey":"chart_8a2b1c","eventKey":"evt_q3-2026-jazz-night","objectLabel":"A-12","status":"booked","bookedAt":"2026-10-02T18:46:00.000Z","extraData":{"orderId":"ord_98a2"}},{"chartKey":"chart_8a2b1c","eventKey":"evt_q3-2026-jazz-night","objectLabel":"A-13","status":"booked","bookedAt":"2026-10-02T18:46:00.000Z","extraData":{"orderId":"ord_98a2"}}]}3. Release seats
Give seats back to inventory with
POST /events/{eventKey}/seats/release, passing the labels in
objectLabels[]. Released seats return to free. The 200 response
reports the releasedAt timestamp and each freed label.
POST /api/v1/events/evt_q3-2026-jazz-night/seats/release HTTP/1.1
Host: seatbuilder.org
X-Api-Key: sk_live_xxx
Content-Type: application/json
{"objectLabels":["A-12","A-13"]}{"chartKey":"chart_8a2b1c","eventKey":"evt_q3-2026-jazz-night","status":"free","releasedAt":"2026-10-02T19:15:30.000Z","released":[{"objectLabel":"A-12"},{"objectLabel":"A-13"}]}4. Read seat status
Poll the current state of every seat in an event with
GET /events/{eventKey}/status. This is a read-only call (no request
body) — a public key is enough. Each object reports its status, its
categoryKey, and, for held seats, the holdToken and holdExpiresAt
(both null once the seat is booked).
GET /api/v1/events/evt_q3-2026-jazz-night/status HTTP/1.1
Host: seatbuilder.org
X-Api-Key: pk_live_xxx{"eventKey":"evt_q3-2026-jazz-night","objects":[{"objectLabel":"A-12","status":"held","holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","holdExpiresAt":"2026-10-02T18:55:00.000Z","categoryKey":"premium","extraData":{"orderId":"ord_98a2"}},{"objectLabel":"A-13","status":"booked","holdToken":null,"holdExpiresAt":null,"categoryKey":"premium","extraData":{"orderId":"ord_98a3"}}]}5. Read the event
Fetch an event's metadata — including the chart it renders and the
published chart version it is pinned to — with GET /events/{eventKey}.
Also a read-only call.
GET /api/v1/events/evt_q3-2026-jazz-night HTTP/1.1
Host: seatbuilder.org
X-Api-Key: pk_live_xxx{"eventKey":"evt_q3-2026-jazz-night","chartKey":"chart_8a2b1c","chartPublishedVersion":3,"name":"Friday jazz night, October 2026","createdAt":"2026-09-18T11:30:00.000Z","updatedAt":"2026-09-18T11:30:00.000Z"}Putting it together
The authoritative server-side lifecycle is: hold each seat the buyer selects (step 1), book the whole selection in one atomic call at checkout (step 2), and release anything the buyer abandons (step 3). Read status (step 4) to reconcile your own state against the platform, and read the event (step 5) to confirm which chart version is live.
Remember which flow you are in: in SDK checkout the server must
never call /seats/hold (the SDK already holds — start at book),
while in the headless flow the server calls hold then book.
For the full checkout choreography — capturing holds from the SDK,
extending to a payment window, and recovering from a 409 at book time —
see Hold seats and confirm at
checkout. For the webhook that tells your
backend a seat was booked or a hold expired, see
Webhooks.
Core concepts
The mental model behind SeatBuilder — charts vs events, the seat lifecycle, hold-token ownership, and how the browser SDK and the REST API fit together.
General admission
The full GA quantity lifecycle — hold, extend, book, release — with contract-verified request and response JSON, remaining-capacity semantics, and the SDK selection flow.