SeatLayer has two authentication contexts because the buyer and your backend have different authority.
| Surface | Credential | Capability |
|---|---|---|
| Managed public/unlisted Buyer SDK | Event key | Anonymous Public-sale render, select, hold, release |
| Platform/SDK Public sale | Event key + pk_test_… or pk_live_… |
Direct, origin-bound Public-sale bootstrap |
| Private/login/presale/channel Buyer SDK | Event key + short-lived bse_… |
Only the audience scope your backend granted |
| Server API | sk_test_… or sk_live_… |
Inspect holds, book, manage inventory, provision resources, report |
Secret keys belong on your server
Create keys in the dashboard and store the secret immediately—the complete value is shown once.
export async function seatlayerRequest(path, init = {}) {
return fetch(`https://api.seatlayer.io${path}`, {
...init,
headers: {
authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
"content-type": "application/json",
...init.headers,
},
});
}Use your deployment platform’s encrypted server-secret mechanism. Fail startup or the server operation clearly when the variable is missing; do not silently fall back to a client credential.
Test and live mode
Every secret key has one immutable mode:
sk_test_… creates and manages sandbox events. Sandbox holds, conflicts, realtime updates, and webhooks behave like production, but bookings consume no credits and payloads carry livemode: false.
sk_live_… creates and manages live inventory. Real bookings consume credits and payloads carry livemode: true.
An event inherits the mode of the key that creates it. Mode cannot be changed later.
{
"error": "mode_mismatch"
}A test key cannot act on a live event, and a live key cannot act on a sandbox event. Secret-key event lists are filtered to the key’s mode; dashboard sessions list both modes. A list with no matching events returns 200 and an empty events array. Direct same-organization access to an opposite-mode event returns the 403 mode_mismatch response above. Build the complete journey with a test key, then create real events using a live key.
Environment routing
A key may also carry an environment tag such as dev, staging, or prod.
- Events inherit the creating key’s environment.
- Webhook subscriptions can filter delivery by environment.
- Environment is routing metadata, not an event, tenant, or workspace authorization boundary.
- On webhook management routes, an environment-tagged key can manage only subscriptions in that routing scope.
- An unset subscription environment behaves as a wildcard.
Recommended setup:
| Application environment | SeatLayer key | Event mode | Webhook route |
|---|---|---|---|
| Local/CI | Dedicated test key | Test | Test receiver or fixture |
| Staging | Staging test key | Test | Staging endpoint |
| Production | Production live key | Live | Production endpoint |
Do not use an environment tag as proof of tenant or workspace ownership.
Buyer SDK authentication
The Buyer SDK never receives an account secret. For an ordinary Public-sale Platform event, give it the event key and the publishable half of your API key:
const picker = new seatlayer.SeatPicker({
container: "#picker",
event: "ev_9f3a",
publicKey: "pk_test_…",
});
await picker.render();On first render, the SDK sends the public key directly to SeatLayer and repeats
that bootstrap only when its in-memory session needs renewal. SeatLayer verifies
that the key belongs to the event, its test/live mode matches, and the browser’s
exact origin is registered for the account. A successful bootstrap returns a
Public-sale-only bse_… bearer together with the chart and compact inventory
status. The SDK keeps the bearer in memory and reuses it for holds, assets, and
live updates. A public key is publishable, but it is not buyer identity and can
never reveal private channel inventory or authorize booking.
For a login, presale, partner, or other private audience, use an explicit buyer access token provider backed by your authenticated server:
const picker = new seatlayer.SeatPicker({
container: "#picker",
event: "ev_9f3a",
buyerAccessTokenProvider: async ({ reason }) => {
const response = await fetch("/api/seatlayer/buyer-access", {
method: "POST",
credentials: "same-origin",
cache: "no-store",
headers: { "content-type": "application/json" },
body: JSON.stringify({ reason }),
});
if (!response.ok) throw new Error("Unable to open seat selection");
return response.json();
},
});
await picker.render();buyerAccessTokenProvider and buyerAccessToken always take precedence over
publicKey. This lets one integration keep the public key in its normal embed
while switching known buyers onto deliberately scoped inventory without any
anonymous fallback.
Browser buyer routes are rate-limited and intentionally limited to buyer-safe operations. The audience token can read/select only its event scope and cannot book; permanent booking remains unavailable without a server secret.
Browser-scoped session tokens
Three credentials exist that are neither a secret key nor an event key. They are
bound to one origin, short lived, revocable, and stored only as hashes —
SeatLayer cannot read them back out of its own database. Your trusted backend
requests dse_…, mse_…, and private-audience bse_… sessions with sk_….
The Public-sale SDK bootstrap is the narrow exception: SeatLayer creates a
Public-only bse_… after validating the publishable key and registered origin.
| Prefix | What it authorizes | Issued through |
|---|---|---|
mse_ |
An operator in the embedded control room, limited to an explicit capability list | POST /v1/events/:key/manage-sessions |
dse_ |
A designer in the embedded chart editor | POST /v1/designer/sessions |
bse_ |
A buyer on one event; either Public-only bootstrap or an explicit sales-channel scope | Direct SDK bootstrap for Public sale, or POST /v1/events/:key/buyer-access-sessions for scoped audiences |
mse_ and bse_ are not interchangeable, and neither is a weaker secret key.
An operator token grants organizer capabilities on an event; a buyer token grants
nothing but the right to see and select a particular audience’s inventory. A
buyer token can never book — booking stays with your server.
Send a buyer token as Authorization: Bearer bse_… on buyer routes after
bootstrap. The SDK handles that header. A Platform/SDK Public-sale embed obtains
its token directly with publicKey; private/login/presale/channel inventory
still requires a token/provider backed by your server. Omitting both from a
Platform event returns the same 404 as an unknown event. Managed public/unlisted
events retain their anonymous Public-sale flow. Sending a bad token is always an
error, never a downgrade: an expired, revoked, wrong-origin, or wrong-event token
fails with a specific code rather than quietly becoming an anonymous shopper.
Rotation checklist
- Create the replacement key in the same mode and environment.
- Store it in the server secret manager.
- Deploy the backend using the replacement.
- Exercise an authenticated read or sandbox booking.
- Confirm relevant webhook routing.
- Revoke the old key after traffic has moved.
- Investigate logs if rotation followed suspected exposure.
Common mistakes
- Using a
PUBLIC_,NEXT_PUBLIC_, orVITE_environment variable for the secret. - Creating a live event during staging tests.
- Assuming the visible key prefix is the enforcement mechanism.
- Treating an environment or
externalRefas tenant authorization. - Logging the complete
Authorizationheader during request debugging.
Continue to Install the Buyer SDK or start the Quickstart.