SeatBuilderSeatBuilder Docs

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/status

Every 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.

Server-side HTTP reference — SeatBuilder Docs