Skip to content
Docs
Concept

How sales work

Every kind of caller - a buyer on a website, in an app, on a partner's site, a box office operator, a promoter, an outlet, a kiosk - lists what is on sale, makes an order, authenticates a customer and reads the order back the same way. What differs is the credential.

Ticketlayer sells through many kinds of caller and manages three classes of credential. This section draws each caller through the same four moments so the pages can be read side by side:

  1. List what is on sale
  2. Make an order
  3. Authenticate a customer
  4. Read the order back

What differs from page to page is the credential: which one authenticates the request, where the sales channel comes from, and where a customer token escalates a session rather than authenticating it. The diagrams make that visible on every arrow.

Three questions every request answers#

QuestionAnswered byNever answered by
Who is calling, and how much do we trust them?the credentialthe request body
Under what commercial rules are they selling?the sales channelthe model, or a path segment the caller picks
Which buyer is this for?the customer token, carried on topthe credential alone

A fourth thing follows and removes a false choice: the account is the business relationship, the channel is the terms it sells under, and the credential is only how a given actor arrives. An outlet has staff signing in to Backstage with a staff JWT and an EPOS calling the API with a channel secret key, on the same account, the same channel and the same access. "Is this staff or a partner" is never the question; it is two credentials reaching one business relationship.

Three credential classes#

They differ by where they may be held, not by what they reach.

ClassCredentialWhere it may be held
Low trustThe channel publishable key, tlpk_A browser, a native app binary, a site builder: anywhere a user can read it. It names one channel and reaches the low-trust surface, whose responses are built field by field so nothing a view does not declare can leak.
Medium trustThe channel secret key, tlsk_A server, never a browser. It names one channel and reaches what a storefront server needs: the channel-scoped reads, carts, checkout, payments, the channel's own orders and customer records. Nothing else.
High trustThe staff JWT (the identity issuer), the organisation API key tlak_, the device JWT, the embed session and the service JWTBackstage, staff tools, paired devices and your own server. These reach the staff surface, gated by permissions.

Two things are carried on top of a credential and never authenticate on their own:

  • The customer token. Thirty minutes, with a 24 hour absolute ceiling that a refresh carries forward rather than extends. It is obtained by a magic link (a 15 minute code) or by a partner identity assertion. There is no customer principal: the channel key authenticates the request and the handler reads the customer from the verified token. A request that needs an identity and has none is refused with CUSTOMER_TOKEN_REQUIRED. That is why a forged body cannot widen scope: identity comes from the token, never from input.
  • The order access token. Emailed with an order, it grants access to that one order. A Live session holds them per order.

Where the channel comes from#

The channel is a filter on the ordinary resources, not a resource of its own. GET /events with no channel is every event the caller may see; with a channel it is what that channel lists as discoverable right now, with each event's listing state and the price the channel resolves for it. A cart has no meaning without a channel, so POST /cart requires one. There is no catalogue endpoint and no listings read for selling. Listings are created, edited and removed at /sales-channels/{channelId}/listings, which is channel administration, not selling.

The channel is taken from the credential when the credential is channel-scoped, and from a header when it is not:

  • A channel key (tlpk_ or tlsk_) names its channel. Nothing is supplied.
  • A staff JWT, an organisation API key or a device JWT names an organisation but not a channel, so the request carries X-Ticketlayer-Channel: <slug>. It is a slug, not an id, because ids differ between test and live configuration and slugs do not.

A caller acts only on the channels it holds. A channel key holds exactly one. A staff credential holds the set of channels it has been granted access to, unless its role grants the permission at organisation scope. Box office staff and promoters sell at channel scope and may act only on channels they have access to; organisation owners, venue admins and event managers hold the same permissions at organisation scope and see everything. Naming a channel outside the set is a 403. Naming none, where the permission is granted at channel scope, is a 400, never a silent narrowing. Channel access is the boundary; the filter is derived from it, so the filter is never the thing withholding data.

Discoverable, visible, sellable#

An event on a channel is in three states, and every question a buyer or an app asks is one of them:

StateThe questionWhere it is asked
DiscoverableDoes it appear in a list?GET /events with a channel, GET /sales/listings, event cards
VisibleCan it be opened by id?GET /events/{id} and its occurrences, prices and layout, GET /sales/events/{id}
SellableWill a cart accept it?POST /cart/{id}/items, checkout

Sellable implies visible, and discoverable implies visible. A hidden event is visible and sellable but not discoverable: the link works, the list does not show it. An announced event is discoverable and visible but not yet sellable: it has a card, and the card says when. Off is none of the three.

The rules live on the event and are inherited by its listings. The event carries one rule per state, the organisation's intent for it on every channel. A listing (the event on one channel) may override any of the three for that channel, in either direction: the venue's own site opens a week earlier, the outlet a week later. Set once on the event, refine per channel only where a channel differs.

A rule is one shape, from a flag to a targeted schedule:

{ "open": true, "windows": [
  { "from": "2026-10-01T09:00:00Z", "until": null, "audienceTags": [] },
  { "from": "2026-09-24T09:00:00Z", "until": null, "audienceTags": ["member.priority-booking"] }
] }

open: false is closed. open: true with no windows is always. With windows, the rule is true when any window matches now and the buyer: an empty audienceTags is everyone, and a list of customer tags admits buyers who hold any of them. The example above is on sale to members a week before everyone else. (audience, a single tag or null, is the deprecated form of the same field.)

Read the state; never re-derive it. The API evaluates the rules for the caller and sends the result with the event: the listing block on a scoped event read and every sales listing carry discoverable, visible, sellable, opensAt (the next moment it becomes sellable for this buyer) and closesAt. A storefront card says "on sale Friday 10:00" to the buyer in front of it, and a member sees the member date, without a second call.

The rules bind everyone, staff included. The one exception is a permission: a caller holding events.list_hidden on the channel (box office staff and door scanners hold it) sees everything the channel lists whatever the rules say right now, each flagged with its real state so the app can mark it hidden or not yet on sale. A door has to admit the holders of an invitation-only event too. Selling is never excepted.

For the simplest setup, a sales channel with autoList on lists every event of its account when the event is published, inheriting the event's rules; the website channel you are given at signup is one. Publish the event, set its rules or leave them open, and it is on sale.

Two surfaces, and Live at the edge#

  • The low-trust surface is /sales/* on the Live API, reached only with a channel key. Every response is a declared view.
  • The high-trust surface is the staff endpoints of the Backstage API, with full domain shapes.

Live is the edge, and every channel-key call goes through it. It holds the publishable key on behalf of a browser session and fronts the queue and the rate limiter, so a browser client never sees a key. A server selling under a secret key calls Live too. The Backstage API serves the same operations to Live alone, as its upstream, not to a page, an app or a partner, whatever key they hold. A server with a high-trust credential talks to the Backstage API directly.

How to read the diagrams#

Each page has one sequence diagram. Solid arrows are calls, dashed arrows are replies, and the badge under an arrow is what that call carries:

  • Amber - low trust: the publishable key.
  • Blue - medium trust: the secret key.
  • Violet - high trust: a staff JWT, an organisation API key, a device JWT.
  • Teal - where the channel comes from: the key itself, the Host of a hosted storefront, or X-Ticketlayer-Channel.
  • Green - carried on top and never authenticating: a customer token, a partner assertion, an order access token.
  • Grey - not a credential at all: a session id, a local token.

The channel types#

WhoCredential that authenticatesWhere the channel comes fromCustomer
A buyer on your websitetlpk_, held by Livethe keymagic link; the token stays in the Live session
A buyer on the hosted box officetlpk_, held by Livethe storefront's Hostmagic link; the token stays in the Live session
A buyer in a native apptlpk_, in the app binarythe keymagic link; the token stays in the Live session
A partner's own storefronttlsk_, on the partner's serverthe keya partner identity assertion signed by the partner's IdP
Staff at a box office or a tillstaff JWTX-Ticketlayer-Channelusually none; "on behalf of" is not authentication
A promoterstaff JWTX-Ticketlayer-Channelusually none
An outlet or resellerstaff JWT and tlsk_, one accountheader and key, same channelusually none; the sale posts a receivable
A kioskdevice JWTX-Ticketlayer-Channelmagic link on screen, or none

Keys and authentication is the reference for the headers each credential uses.