Skip to content
Docs
Reference

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.

CredentialPrefixWhere it may be heldHeader
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 APIAuthorization: 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 recordsAuthorization: Bearer tlsk_...
Organisation API key (high trust)tlak_Your serverAuthorization: Bearer tlak_... plus X-Ticketlayer-Org: <slug>
Staff access token (high trust)JWTBackstage, staff tools, MCP agentsAuthorization: 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-Org header 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 /events with it returns what that channel sells, and POST /cart requires it. The same header serves a staff token and a device credential.
  • Each route checks the key's scopes; a missing scope is 404, not 403 (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 at https://auth.ticketlayer.com/.well-known/jwks.json.
  • The flow. Send the person to /oauth/authorize with your client_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/token for an access token and a refresh token.
  • The token. An RS256 JWT, valid for 15 minutes, with iss https://auth.ticketlayer.com, aud backstage-api, and the organisation the person chose as org_slug. Send it as Authorization: Bearer <jwt> with X-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.write and so on).
  • Clients. An MCP client registers itself through Dynamic Client Registration (/oauth/register: public PKCE clients, https or loopback redirect URIs). A token meant for another server names it with the resource parameter (RFC 8707), and that server's address joins backstage-api in the token's aud.

For a server acting on its own, with no person at a browser, use an organisation API key instead.

Environments#

ProductionStaging
Backstage APIhttps://api.ticketlayer.com/v1https://api.staging.t9r.dev/v1
Live APIhttps://live.ticketlayer.comhttps://live-staging.t9r.dev
Hosted checkouthttps://checkout.ticketlayer.comhttps://hbo.staging.t9r.dev
CDNhttps://cdn.ticketlayer.comhttps://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.