Server-side HTTP reference
Language-neutral REST reference — base URL, X-Api-Key auth, and copy-paste curl for hold/extend/book/release/status. For any backend (PHP, Python, Ruby, Go).
Server-side HTTP reference
This is a language-neutral reference for driving SeatBuilder directly over
HTTP — for backends with no JavaScript SDK (a PHP/Guzzle service, a Python
worker, a Ruby or Go API). Every operation below is a plain REST call you
can reproduce in any HTTP client, shown here as copy-paste curl.
Use this page when your server owns the whole seat lifecycle (the headless / server-authoritative flow): it holds seats, books them at checkout, and releases anything abandoned. For the concepts and the full JSON response shapes, see Hold and book lifecycle; for the two-key credential model and when the server vs. the browser takes the initial hold, see Server-side integration.
Base URL and auth
All calls go to https://seatbuilder.org under the global prefix
/api/v1. Authenticate every request with the X-Api-Key header — there
is no OAuth, session cookie, or JWT for API consumers.
Call these routes from your server with your secret key (sk_live_…)
and keep it off the browser. hold and release also accept a public
key (pk_live_…, release only with a holdToken, scoped to that
token's held rows) — the public key exists for browser code where the
value is unavoidably visible. book, extend, and reset are
secret-key only; a public key gets 403 public_key_not_allowed. Read
routes (status, objects, get event) work with either key. See
Authentication for the full key-type matrix.
curl -H "X-Api-Key: sk_live_xxx" \
https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/statusEvery write route takes a canonical objectLabels array — there is no
singular objectLabel field and no separate labels field on /extend.
A single seat is simply a one-element array. The examples below show the
current field for each operation. Every write is naturally idempotent
(no Idempotency-Key header, no retry limit) — see the retry note under
each endpoint. See Error model for the 4xx/5xx
envelope and the full error code table.
Hold a seat
Take a hold on one or more seats. POST /api/v1/events/{eventKey}/seats/hold
accepts an objectLabels array plus your session holdToken. ttlSeconds
is optional and defaults to 900 (15 minutes) when omitted.
# Hold one or more seats atomically (all-or-nothing) with objectLabels[]:
curl -X POST https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/seats/hold \
-H "X-Api-Key: sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","ttlSeconds":900}'objectLabels[] holds every listed seat atomically under one holdToken
— if any seat cannot be held the whole request fails 409 seat_unavailable and nothing is held. The response always mirrors the
input shape: an array objects[], whether you hold one seat or many. A
GA-area hold rides on a single-element objectLabels[] plus
objectType: "generalAdmission" and quantity (see General
admission).
Retries are safe. A label your token already (live-)holds is a
no-op — 201 again, expiry unchanged. For a GA area, quantity is the
token's total desired reservation: the same quantity is a no-op; a
different quantity resizes the hold (409 ga_capacity_exceeded if the
new total doesn't fit).
Extend a hold
Refresh the expiry of seats a token already holds (for example when the
buyer reaches the payment screen). POST /api/v1/events/{eventKey}/seats/extend
takes an optional objectLabels array — omit it to extend every seat
currently held by the token. ttlSeconds is capped at 3600 (1 hour).
curl -X POST https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/seats/extend \
-H "X-Api-Key: sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2","ttlSeconds":600}'Partial-success: a label the token no longer (live-)holds comes back in
failed[] with reason: already_booked | hold_expired | not_held_by_token
without failing the rest. Only 409 no_active_hold — the token has
nothing at all on the event — fails the whole call.
Book the held seats
Promote holds to permanent bookings in one atomic call.
POST /api/v1/events/{eventKey}/seats/book takes an objectLabels array
plus the shared holdToken — it is all-or-nothing: every listed label
must currently be held by that token or the whole request fails (409 not_held_by_token, or 410 hold_expired if the token's own hold had
lapsed — also after the expiry sweep: an expired hold is remembered for
its token until the seat is held again by any token or an operator
force-releases or changes it; GA areas behave the same). Secret key only — a public key gets 403 public_key_not_allowed.
Retrying with every label already booked under the same token returns
200 with the identical booked[] body.
curl -X POST https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/seats/book \
-H "X-Api-Key: sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2"}'Release seats
Give seats back to inventory with
POST /api/v1/events/{eventKey}/seats/release. Pass holdToken to scope
the release to that token — objectLabels becomes optional (omit it
to release everything the token holds/booked on the event). A public
key must always pass holdToken and only frees held rows under it —
never a booked seat. A secret key may omit holdToken for individual
seats (admin release of any held/booked row); a general-admission area
always requires holdToken (400 hold_token_required).
curl -X POST https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/seats/release \
-H "X-Api-Key: pk_live_xxx" \
-H "Content-Type: application/json" \
-d '{"objectLabels":["A-12","A-13"],"holdToken":"7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2"}'Labels already free come back 200 in alreadyFree[], not as an error.
GA entries in released[] carry the freed quantity. If any label is
held/booked by a different token (or booked, for a public key) the whole
call fails 409 not_held_by_token and nothing is released.
Reset a sandbox event
Wipe an event's seat state entirely for repeatable test runs.
POST /api/v1/events/{eventKey}/reset — secret key, sandbox events
only (403 sandbox_only against a production event). Deletes every
hold/booking on the event; no webhooks fire.
curl -X POST https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-sandbox/reset \
-H "X-Api-Key: sk_test_xxx"{"eventKey":"evt_q3-2026-jazz-sandbox","reset":true,"removed":12}Read seat status
Poll the current state of every seat in an event.
GET /api/v1/events/{eventKey}/status is a read-only call with no request
body — a public key suffices.
curl https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/status \
-H "X-Api-Key: pk_live_xxx"Pass ?labels=A-12,A-13 (comma-separated, max 200) to narrow the status
response to specific seats for large venues, and ?holdToken=<token> to
flag your own live holds (mine: true on a seat, mineQuantity on a GA
area) without the response ever including anyone's token:
curl "https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/status?labels=A-12,A-13&holdToken=7f6c3a91-b1ef-4d0a-9c84-3f12a8e0b5d2" \
-H "X-Api-Key: pk_live_xxx"Free seats are still omitted (they're the implicit default) — use
/objects for the full layout including
free seats. An expired-but-unswept hold is already reported free here.
extraData is included only for secret-key callers. See Hold and book
lifecycle for the
full seat/GA entry shapes.
List objects and categories
Read the full published-chart layout — every object plus its category
definitions — independent of live status.
GET /api/v1/events/{eventKey}/objects is a read-only call (a public key
suffices). Use it to map each categoryKey to your own price, including for
free seats, before any seat is held.
# Read the full published-chart layout (objects + categories) independent of live status —
# use it to map each categoryKey to your own price, including for FREE seats:
curl https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/objects \
-H "X-Api-Key: pk_live_xxx"
# Narrow to specific seats with ?labels= (comma-separated, max 200):
curl "https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night/objects?labels=A-12,A-13" \
-H "X-Api-Key: pk_live_xxx"The response is
{ eventKey, categories: [{ key, label }], objects: [{ objectLabel, categoryKey, objectType, ... }] }.
An unpublished chart returns empty arrays ({ "categories": [], "objects": [] })
with a 200 — not a 404. Unknown labels passed to ?labels= are silently
omitted rather than rejected.
Get event
Fetch an event's metadata, including the chart it renders and the
published chart version it is pinned to. GET /api/v1/events/{eventKey}
is also a read-only call.
curl https://seatbuilder.org/api/v1/events/evt_q3-2026-jazz-night \
-H "X-Api-Key: pk_live_xxx"Response shapes
For the full JSON request and response bodies of each endpoint —
including the holdExpiresAt, booked[], releasedAt, and status
object shapes — see Hold and book
lifecycle, which documents every
response verbatim from the API contract.