Authentication
How API keys authenticate every SeatBuilder REST call — public vs secret keys, the X-Api-Key header, environments, and workspace scope.
Authentication
Every request to the public SeatBuilder REST API authenticates with a single
header: X-Api-Key. Keys are workspace-scoped and bound to a single
environment (production or sandbox). There is no OAuth, no session
cookie, and no JWT for API consumers — those flows are reserved for the
dashboard UI.
API key types
Each workspace uses two kinds of key per environment:
| Prefix | Purpose | Where it lives |
|---|---|---|
pk_live_* | Publishable — safe to embed in browser code | SDK publicKey option |
sk_live_* | Secret — server-side only | Backend env var (SEATS_API_KEY) |
pk_test_* | Publishable, sandbox environment | SDK publicKey during testing |
sk_test_* | Secret, sandbox environment | Backend env var during testing |
How keys are provisioned differs by type. Your publishable
(pk_live_* and pk_test_*) keys are seeded automatically when the
workspace is created and are usable immediately — the value you see on
the dashboard API keys page is
the full key, so you can copy it straight into your SDK publicKey
option.
Secret (sk_live_* and sk_test_*) keys are not created
automatically. Create each secret key on demand from the dashboard
API keys page, where its plaintext
value is shown exactly once at creation time. Copy it into your
backend secret store right away — the value is never displayed again. If
you lose a secret key, rotate it from the same page to mint a
replacement (the old value stops working immediately). Create your
secret key before you need it server-side.
Publishable (pk_*) keys are scoped to the buyer-facing seat-selection
routes, and only part of that surface — some seat actions are secret-key
only:
| Endpoint | pk_* (public) | sk_* (secret) |
|---|---|---|
GET event, GET /status, GET /objects | Yes | Yes |
POST /seats/hold | Yes | Yes |
POST /seats/book | No — 403 public_key_not_allowed | Yes |
POST /seats/extend | No — 403 public_key_not_allowed | Yes |
POST /seats/release | Only with holdToken, and only frees rows status: 'held' under that exact token — booked seats are never released by a public key | Yes — with a holdToken (held + booked) or without one (any held/booked seat, admin release) |
POST /events/{eventKey}/reset | No — 403 public_key_not_allowed | Yes — sandbox events only (403 sandbox_only in production) |
| Chart creation, webhook management, and everything else | No | Yes |
Use publishable keys in browser code where the key value is visible to
the buyer. book and extend are server-side actions by design — a
buyer's browser can select and hold seats, but only your backend
confirms the sale or extends the payment window. Never ship sk_* keys
to a browser. See Error model for the exact code
values above.
The X-Api-Key header
Pass your key in the X-Api-Key request header. The server hashes the
key on receipt and looks it up in the workspace's key table.
curl -H "X-Api-Key: sk_live_xxx" \
https://seatbuilder.org/api/v1/chartsCalls with a missing or malformed key return 401 Unauthorized. Calls
with a syntactically valid key whose hash isn't on file return
401 Unauthorized as well — the server does not distinguish unknown
keys from forged keys to keep enumeration noisy. See
Error model for the full error envelope.
Environments
Each workspace has two isolated environments: production and
sandbox. Keys cannot cross environments. A pk_test_* key cannot read
production events, and a pk_live_* key cannot reach sandbox data.
Isolation applies to events, holds, and bookings — not to charts.
Events, seat holds, and bookings are strictly environment-scoped: a
*_test_* key can never read or write a production event's seats, and
vice versa. Charts, however, are a workspace-level design asset:
GET /charts lists every chart in the workspace regardless of which
environment(s) its events use, and both a pk_live_* and a pk_test_*
key can read the same chart's published layout. This is deliberate — a
chart is a seat-map template you design once and reuse across
production and sandbox events, not per-environment data.
In practice this means:
- Develop and test against
*_test_*keys hitting your sandbox environment. Seat state — holds, bookings,/status— never leaks between environments. - Promote to
*_live_*only when your integration is verified — the sandbox is rate-limited and may be reset (seePOST /events/{eventKey}/resetin Hold and book lifecycle). - Don't rely on a chart key being environment-scoped — it isn't. Scope your own access control around events, not charts, if that distinction matters to your integration.
Workspace scope
Keys are bound to one workspace. Every authenticated request
auto-scopes to that workspace's data — charts, events, webhooks, and
seat state are all isolated from other workspaces. There is no API
surface for reading data across workspaces; misconfigured
cross-workspace requests return 403 Forbidden.
This is the only safe assumption a multi-tenant integration can make: if a buyer presents a hold token from another workspace's event, the book request rejects with 403 before any state mutation runs.