SeatBuilderSeatBuilder Docs

Server-side integration

The secret-key backend flow — where sk_live may and may not appear, the public key as the browser render credential, and the two checkout flows (SDK checkout vs headless).

Server-side integration

SeatBuilder has two keys, and getting the split right is the whole of server-side integration. This guide covers the auth model, why the public key is the only credential the browser needs, and how your backend drives the authoritative write calls. It also disambiguates the two checkout flows so you never accidentally double-hold a seat. For the concepts behind holds and bookings, see Core concepts.

The two-key auth model

KeyPrefixLivesUsed for
Secret keysk_live_xxxServer onlyEvery write call (hold, book, release, extend), webhook registration.
Public keypk_live_xxxBrowser (safe)SDK render + reading seat status.

Your public key is seeded automatically and is ready to use. Your secret key is not — create it on demand from the dashboard API keys page, where its value is shown exactly once at creation. Copy it into your server-side secret store immediately; if you lose it, rotate from the same page to mint a replacement.

Both are sent in the X-Api-Key header. The rule that matters:

The secret key sk_live_xxx must never reach the browser. If it ends up in client-side JavaScript, a page source, or a public repo, anyone can book and release seats on your behalf.

The browser needs only the public key. The public key is the browser render credential — it is designed to be exposed, scoped to your workspace and environment, and it authenticates exactly two things: read calls (fetching the event and seat status) and taking holds through the SDK. There is nothing else to mint or inject client-side; hand the SDK your public key and it renders. The long-lived secret never crosses to the untrusted browser.

Render tokens are an internal dashboard mechanism, not part of external integration. The render-token endpoint is guarded by a dashboard session (cookie-JWT) and exists only for the in-app LiveSeatViewer preview. A secret key cannot call it, and the SDK's render() has no field to accept one — so ignore it entirely when integrating from your own site.

Which flow are you building?

There are two supported checkout flows. Pick one — the difference is who takes the hold, and mixing them up is the most common integration bug (re-holding a seat the SDK already holds returns a 409).

SDK checkout

The browser SDK auto-holds each seat the buyer selects and captures a holdToken client-side. Because the seat is already held by the SDK, your server must never call POST /seats/hold for it. Your server's job is only to:

  • extend the hold while the buyer moves through your payment window,
  • book the selection at checkout, and
  • release anything the buyer abandons.

Pass the SDK's holdToken to your backend (e.g. in your create-order request) and drive extend / book / release with it — from your server, with your secret key. See Hold and book lifecycle for the exact extend / book / release contracts, and skip the hold step.

Headless / server-authoritative

No JS SDK on the page — your server owns the entire lifecycle. It calls hold first, then book at checkout (and release for abandoned seats). This is the flow the Server-side HTTP reference serves for backends with no JavaScript SDK (PHP, Python, Ruby, Go), and it is walked endpoint-by-endpoint in Hold and book lifecycle.

Driving write calls from the server

Whichever flow you build, the write calls (hold / book / release / extend) should go out from your server. The API accepts either key on these routes, but calling them from your server with your secret key keeps the secret off the browser — that is the whole point of the split. A typical headless checkout does:

  1. Your server takes the hold with POST /api/v1/events/{eventKey}/seats/hold using X-Api-Key: sk_live_xxx, so the seat is locked immediately.
  2. At checkout your server books the whole selection in one atomic POST /api/v1/events/{eventKey}/seats/book call, again with the secret key.
  3. Anything abandoned is returned with POST /api/v1/events/{eventKey}/seats/release.
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"}}

In the SDK-checkout flow, skip step 1 — the SDK already holds the seat — and drive only extend / book / release with the SDK's holdToken.

The full request and response contract for every write and read endpoint lives in Hold and book lifecycle. To react to seat changes your server did not initiate — a hold expiring, a booking from another integration — subscribe to Webhooks.

Webhook signature header

Webhook deliveries are signed with the SeatBuilder-Signature header. See Webhooks for the HMAC verification steps.

Checklist

  • Secret key sk_live_xxx is only ever read from server-side secrets, never embedded in client code.
  • The browser renders with the public key alone — it is the render credential; there is no browser-side render token to mint.
  • Pick one flow: SDK checkout (server never calls /seats/hold — only extend / book / release) or headless (server calls hold then book).
  • All write calls go out from your server with the secret key.
  • Webhook deliveries are verified via the SeatBuilder-Signature header before you trust them (see Webhooks).
Server-side integration — SeatBuilder Docs