Keys and authentication
Publishable keys for the browser, organisation API keys for your server, OAuth through the identity issuer for people and their apps: which credential goes where, and the headers each one uses.
Ticketlayer has three classes of credential, and they differ by where they may be held, not by what they reach. Pick by where the code runs.
| Credential | Prefix | Where it may be held | Header |
|---|---|---|---|
| Publishable key (low trust) | tlpk_ | A browser, a native app binary, a site builder: anywhere a user can read it. Names one sales channel; reaches the low-trust surface, the Live API | Authorization: Bearer tlpk_... (or X-Channel-Key) |
| Secret key (medium trust) | tlsk_ | Your server, never a browser. Names the same one sales channel; reaches what a storefront server needs on the Live API: the channel-scoped reads, carts, checkout, payments, the channel's own orders and customer records | Authorization: Bearer tlsk_... |
| Organisation API key (high trust) | tlak_ | Your server | Authorization: Bearer tlak_... plus X-Ticketlayer-Org: <slug> |
| Staff access token (high trust) | JWT | Backstage, staff tools, MCP agents | Authorization: Bearer <jwt> plus X-Ticketlayer-Org: <slug> |
Both channel keys are issued on the channel in Backstage (Sales channels, the channel, Keys) and shown once. A customer token is not a credential: the channel key authenticates the request, and the customer token, carried on top of it, escalates the session to one buyer. There is no customer principal and no route accepts a customer token on its own. How sales work draws every kind of caller through the same four moments and shows which credential carries each call.
Publishable keys (browser)#
The Live SDK and the CDN loader take the key once and send it on every request; you do not write the header yourself.
<script src="https://cdn.ticketlayer.com/v1/ticketlayer.js"
data-publishable-key="tlpk_..."
data-api-url="https://live.ticketlayer.com"
data-checkout-base="https://checkout.ticketlayer.com"></script>import { createLiveClient } from '@ticketlayer/live';
const tl = await createLiveClient({ baseUrl: 'https://live.ticketlayer.com', publishableKey: 'tlpk_...' });Calling the Live API directly:
curl https://live.ticketlayer.com/sales/listings \
-H "Authorization: Bearer tlpk_..."Browsing needs nothing more. The first cart creates a Live session; the SDK
holds its id and sends it as X-Live-Session (or a cookie on a hosted
storefront). Customer sign-in is a magic link: POST /sales/customer-auth/magic-link,
then POST /sales/customer-tokens with the code; <tl-login> does both. The
customer token that comes back escalates the Live session to that buyer; the
publishable key still authenticates every request.
Where to get one: Backstage, Sales channels, the channel, Keys, Create key, Publishable (browser). Signing up creates the channel and the key for you; see Get a key.
Secret keys (server)#
A secret key is issued on the same channel, for a server that sells on the channel's behalf: a partner's storefront, the EPOS of an outlet, your own backend. It authenticates to the Live API, like a publishable key, and names its channel itself, so no channel header is sent. Send it to Live, not to the Backstage API:
curl https://live.ticketlayer.com/sales/listings \
-H "Authorization: Bearer tlsk_..."It reaches the channel-scoped reads, carts, checkout, payments, the channel's
own orders and the customer records it creates, and nothing administrative.
An order on another channel is a 404, not a 403. Signing a buyer in from a
server is a partner identity assertion; see
A partner's own server-side storefront.
Organisation API keys (server)#
An organisation API key is created by a staff user in Backstage (or with
POST /v1/api-keys while signed in) with a name and a list of scopes such as
events.write, orders.read, saleschannels.write. The key material is
returned once.
curl https://api.ticketlayer.com/v1/events \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug"With the SDK:
import { BackstageClient } from '@ticketlayer/backstage';
const backstage = new BackstageClient({
baseUrl: 'https://api.ticketlayer.com',
accessToken: process.env.TICKETLAYER_API_KEY, // tlak_...
organisationSlug: 'your-org-slug',
});
const { events } = await backstage.events.list({ limit: '20' });Rules the API applies to a tlak_ bearer:
- The key is looked up by hash; revoked or expired keys get
401. - The
X-Ticketlayer-Orgheader must name the key's own organisation. - The key names no sales channel. Where a call sells through one, the request
carries
X-Ticketlayer-Channel: <channel slug>:GET /eventswith it returns what that channel sells, andPOST /cartrequires it. The same header serves a staff token and a device credential. - Each route checks the key's scopes; a missing scope is
404, not403(the platform never confirms that something exists to a caller who cannot see it). - Routes that are for signed-in people only (
/v1/me/*,/v1/api-keys) refuse API keys.
API keys also mint embed sessions (POST /v1/embed/sessions): short-lived
tokens pinned to one account that let you mount Backstage widgets in your own
app with @ticketlayer/backstage/embed. See the Backstage SDK
getting started.
Staff tokens (OAuth)#
People sign in to Ticketlayer's identity issuer, https://auth.ticketlayer.com,
with OAuth 2.1 (authorization code with PKCE). Backstage signs its staff in
there, and so does every app that acts for a person: the
MCP server's agents, and your own integrations.
- Discovery. Everything a client needs is in
https://auth.ticketlayer.com/.well-known/oauth-authorization-server(the same document is at/.well-known/openid-configuration): the authorize, token, revocation and registration endpoints, and the JWKS athttps://auth.ticketlayer.com/.well-known/jwks.json. - The flow. Send the person to
/oauth/authorizewith yourclient_id, the scopes you want and a PKCE challenge. They sign in, choose an organisation and consent; your redirect URI receives a one-time code, which you exchange at/oauth/tokenfor an access token and a refresh token. - The token. An RS256 JWT, valid for 15 minutes, with
isshttps://auth.ticketlayer.com,audbackstage-api, and the organisation the person chose asorg_slug. Send it asAuthorization: Bearer <jwt>withX-Ticketlayer-Org: <org_slug>. Refresh tokens rotate on every use; revoke one at/oauth/revoke. - Scopes cap the person. A token can do what its scopes allow and what the
person's own role allows, never more. The scopes are the same granular
catalogue organisation API keys use (
events.read,orders.read,webhooks.writeand so on). - Clients. An MCP client registers itself through Dynamic Client
Registration (
/oauth/register: public PKCE clients,httpsor loopback redirect URIs). A token meant for another server names it with theresourceparameter (RFC 8707), and that server's address joinsbackstage-apiin the token'saud.
For a server acting on its own, with no person at a browser, use an organisation API key instead.
Environments#
| Production | Staging | |
|---|---|---|
| Backstage API | https://api.ticketlayer.com/v1 | https://api.staging.t9r.dev/v1 |
| Live API | https://live.ticketlayer.com | https://live-staging.t9r.dev |
| Hosted checkout | https://checkout.ticketlayer.com | https://hbo.staging.t9r.dev |
| CDN | https://cdn.ticketlayer.com | https://cdn.staging.t9r.dev |
Staging is for Ticketlayer's own development; the credentials in one environment are not valid in the other.
Versioning#
Every request may carry TL-Version: 2026-06-13 (the dated API version). The
SDKs send it for you; a request without it gets the organisation's default,
so generated code keeps working across releases. See
API versions.