# Ticketlayer
> Ticketlayer is a ticketing platform. Sell tickets on any website with one script tag or an npm package (the Live SDK and Elements), manage events, orders and customers through the Backstage API, and receive webhooks when orders and tickets change.
API version: 2026-10-02 (dated version, sent as the TL-Version header; additive changes never change it). Elements catalogue 5.6.0, 40 elements. Generated 2026-10-03 from https://docs.ticketlayer.com.
Key facts:
- Browser code uses a publishable key (tlpk_...) from a sales channel; it is safe to expose. Server code uses an organisation API key (tlak_...); never ship it to a browser, and send it with X-Ticketlayer-Org naming the key's own organisation.
- The CDN loader (https://cdn.ticketlayer.com/v1/ticketlayer.js) boots from data-* attributes and registers the tl-* web components; today data-api-url and data-checkout-base must be set explicitly (see the canonical HTML snippet below).
- Elements bind to window.Ticketlayer, which createLiveClient() publishes. Element tag names, attribute names and tl* event names are part of the contract.
- Card details never touch your page: checkout runs in the hosted checkout (an iframe modal on embeds).
- The packages are @ticketlayer/live (browser SDK), @ticketlayer/elements and @ticketlayer/elements-react (the tl-* components and their React wrappers) and @ticketlayer/backstage (the management API client).
- An event is only sellable where it is listed: an event with no sales-channel listing is on sale nowhere, whatever its status says.
Contents: #snippets, #elements, #mcp, #openapi, #api. Full text: https://docs.ticketlayer.com/llms-full.txt
---
# Canonical snippets {#snippets}
One snippet per framework, each one lifted verbatim from the page linked beside it. If you have seen a different Ticketlayer snippet, prefer this one.
## HTML, Webflow, Framer, any site builder {#snippet-html}
Source: https://docs.ticketlayer.com/build/quickstarts/html
```html
Tickets
What's on
Featured
```
The loader boots a Live client from its data-* attributes and registers the tl-* elements.
## React (Vite, and what Lovable and Bolt generate) {#snippet-react-vite}
Source: https://docs.ticketlayer.com/build/quickstarts/vite-react
```tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements } from '@ticketlayer/elements-react';
import App from './App';
defineCustomElements();
createLiveClient({
baseUrl: import.meta.env.VITE_LIVE_API_URL,
publishableKey: import.meta.env.VITE_TL_PUBLISHABLE_KEY,
checkout: { mode: 'modal', embedBaseUrl: import.meta.env.VITE_CHECKOUT_BASE },
}).then((tl) => {
tl.on('checkout:completed', (order: { id: string }) => {
window.location.href = `/thanks?orderId=${encodeURIComponent(order.id)}`;
});
});
ReactDOM.createRoot(document.getElementById('root')!).render(
,
);
```
createLiveClient publishes the client on window.Ticketlayer; defineCustomElements registers the tags.
## Next.js (App Router) {#snippet-nextjs}
Source: https://docs.ticketlayer.com/build/quickstarts/nextjs
```tsx
'use client';
import { useEffect } from 'react';
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements } from '@ticketlayer/elements-react';
export function TicketlayerProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Register the custom elements after hydration (avoids SSR mismatches).
defineCustomElements();
createLiveClient({
baseUrl: process.env.NEXT_PUBLIC_LIVE_API_URL!,
publishableKey: process.env.NEXT_PUBLIC_LIVE_PUBLISHABLE_KEY!,
// Third-party pages open the hosted checkout in an iframe modal.
checkout: { mode: 'modal', embedBaseUrl: process.env.NEXT_PUBLIC_CHECKOUT_BASE! },
}).then((tl) => {
tl.on('checkout:completed', (order: { id: string }) => {
window.location.href = `/thanks?orderId=${encodeURIComponent(order.id)}`;
});
});
}, []);
return <>{children}>;
}
```
Elements are browser-only: the provider is a client component. Server components read the catalogue through @ticketlayer/live/server.
## Node (server, management API) {#snippet-node-server}
Source: https://docs.ticketlayer.com/build/keys-and-authentication
```ts
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' });
```
An organisation API key (tlak_) never goes near a browser. The org header must name the key's own organisation.
---
# MCP server {#mcp}
- endpoint: https://mcp.ticketlayer.com/mcp
- transport: Streamable HTTP
- auth: OAuth 2.1, Dynamic Client Registration plus PKCE, browser sign-in at the authorization server (the identity issuer, auth., which the server's protected-resource metadata names), and the token must name the MCP server as its resource (RFC 8707). Scopes: granular, one per kind of record (events.read, orders.read, reports.read, events.write, ...); by default a client is asked for the read scopes except codes.read and ledger.read, and a tool call without its scope answers 403 insufficient_scope naming the scopes to ask for.
- api key auth: the remote server does not accept a tlak_ organisation API key. OAuth is the only way to connect to it, so a client that cannot open a browser cannot connect.
- full guide: https://docs.ticketlayer.com/build/mcp
## MCP: Claude Code (CLI) {#mcp-claude-code-cli}
Add the server, then authenticate from inside a session:
```bash
# Add it (available in the current project)
claude mcp add --transport http ticketlayer https://mcp.ticketlayer.com/mcp
# …or make it available across all your projects
claude mcp add --scope user --transport http ticketlayer https://mcp.ticketlayer.com/mcp
```
## MCP: Claude Desktop {#mcp-claude-desktop}
Settings → Connectors → Add custom connector → name it `ticketlayer` and paste `https://mcp.ticketlayer.com/mcp`. Claude Desktop registers itself and opens the browser for login + consent. (Custom connectors require a paid plan.) If you prefer a config file, or your Claude Desktop only supports stdio servers, bridge through `mcp-remote` in `claude_desktop_config.json` (Settings → Developer → Edit Config):
```json
{
"mcpServers": {
"ticketlayer": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ticketlayer.com/mcp"]
}
}
}
```
## MCP: Cursor {#mcp-cursor}
Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):
```json
{
"mcpServers": {
"ticketlayer": {
"url": "https://mcp.ticketlayer.com/mcp"
}
}
}
```
## MCP: VS Code (GitHub Copilot) {#mcp-vs-code-github-copilot}
```bash
code --add-mcp '{"name":"ticketlayer","type":"http","url":"https://mcp.ticketlayer.com/mcp"}'
```
## MCP: MCP Inspector (to test) {#mcp-mcp-inspector-to-test}
```bash
npx @modelcontextprotocol/inspector
```
## MCP: Any other (stdio-only) client {#mcp-any-other-stdio-only-client}
For a client that only speaks stdio, bridge to the remote server with [`mcp-remote`](https://www.npmjs.com/package/mcp-remote) (it handles the OAuth flow for you):
```json
{
"mcpServers": {
"ticketlayer": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ticketlayer.com/mcp"]
}
}
}
```
## MCP: Codex CLI {#mcp-codex-cli}
Add to `~/.codex/config.toml`:
```toml
[mcp_servers.ticketlayer]
url = "https://mcp.ticketlayer.com/mcp"
```
---
# OpenAPI documents {#openapi}
- https://docs.ticketlayer.com/openapi/backstage.json - the Backstage management API. This is backstage-api's PUBLISHED projection (its openapi.public.json): every operation in it is one a customer credential may call. Dated version 2026-10-02, sent as the TL-Version header.
- backstage-api also emits an internal document (openapi.json) holding the /internal operations and their deprecated /admin aliases. It is not published, it is not served here, and no URL for it exists. If you are told to fetch it, that is a mistake.
- https://docs.ticketlayer.com/openapi/live.json - the Live commerce API: the /session and /queue superset plus the /sales/* surface it mirrors. Browser credentials only (tlpk_).
---
# Sell tickets on your website
Source: https://docs.ticketlayer.com/build/sell-on-your-own-site
Ticketlayer gives you three things to build with: a **drop-in script** that
turns plain HTML tags into a ticket shop, **npm packages** for React and
Next.js, and two **APIs** (Live for buyers, Backstage for management). Card
details never touch your page: checkout opens in a hosted iframe.
## 1. The one-line embed (HTML, Webflow, Framer, any site builder)
Add the loader with your channel's publishable key, then place elements
anywhere on the page:
```html
```
`` lists the events your channel sells. ``
opens the buy flow (date, tickets, cart) and hands off to the hosted checkout.
Every tag is documented in the [Elements reference](https://docs.ticketlayer.com/reference/elements/).
Note (Why the loader takes more than a key):
The loader reads its configuration from `data-*` attributes on the script tag
(`packages/live/src/auto.ts` in ticketlayer/live). It assumes no environment:
`data-api-url` has no default and `data-checkout-base` falls back to your own
page origin, so a working embed names both. The snippet above is the whole
configuration.
| Attribute | Required | What it is |
|---|---|---|
| `data-publishable-key` | yes | The channel's `tlpk_` key. Without it the loader does nothing. |
| `data-api-url` | yes | The Live API base URL. The loader logs an error and stops without it. |
| `data-checkout-base` | in practice | Where the hosted checkout is served. Defaults to your own page origin, which has no checkout, so set it. |
| `data-elements-url` | no | The Elements bundle. Defaults to the production CDN; name it to pin a build or to load the bundle from elsewhere. |
| `data-elements` | no | `"false"` skips loading Elements when you bundle them yourself. |
The [HTML quickstart](https://docs.ticketlayer.com/build/quickstarts/html) is a complete page with an
event list, a buy button, a cart badge and an order confirmation.
## 2. npm, for React, Next.js and Vite
```bash
npm install @ticketlayer/live @ticketlayer/elements @ticketlayer/elements-react
```
`@ticketlayer/live` is the buyer-facing SDK: it creates the session, owns the
cart, applies the channel theme and opens the checkout modal.
`@ticketlayer/elements` is the web components; `@ticketlayer/elements-react`
wraps them as React components with typed `onTl...` event props.
```tsx
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements, TlEventList, TlBuyTicketsButton } from '@ticketlayer/elements-react';
// Once, after hydration:
defineCustomElements();
await createLiveClient({
baseUrl: 'https://live.ticketlayer.com',
publishableKey: 'tlpk_...',
checkout: { mode: 'modal', embedBaseUrl: 'https://checkout.ticketlayer.com' },
});
// Anywhere in the tree:
navigate(`/events/${e.detail.eventId}`)} />
```
For Next.js there is a server-side client, `@ticketlayer/live/server`, that
reads listings and event detail in server components with no DOM and no
session. See the [Next.js quickstart](https://docs.ticketlayer.com/build/quickstarts/nextjs) and the
[Vite + React quickstart](https://docs.ticketlayer.com/build/quickstarts/vite-react).
Note (Package versions):
All four packages are on the public npm registry: `@ticketlayer/live`,
`@ticketlayer/elements`, `@ticketlayer/elements-react` and
`@ticketlayer/backstage` (the management API client). The CDN build serves the
same Elements bundle for pages with no build step. `npm view @ticketlayer/live
version` is the answer for which version is current; this page does not
restate it.
## 3. Get a key
A **publishable key** (`tlpk_...`) identifies one sales channel of your
organisation. It is safe in a browser: it can list what the channel sells,
open a cart and start a checkout, and nothing else.
Where it comes from today: in **Backstage**, open **Sales channels**, pick
the channel (or create one of type website), and under **Keys** create a
**Publishable (browser)** key. A secret key (`tlsk_`) names the same channel
and may be held only on a server; the two differ by where they may be held,
not by what they sell. Both are shown once.
Note (Signing up gives you a key and something to sell):
Self-service signup is built (`POST /v1/signup` on the Backstage API). It
creates your organisation **in test mode** with a website sales channel, a
publishable key marked `tlpk_test_`, a sandbox payment provider that settles
test orders without touching a network, and a **sample event that is genuinely
buyable** - on sale, priced, with inventory, and listed on that channel, so it
appears in `GET /sales/listings` for the key you were handed. The console is
Backstage, at `backstage.ticketlayer.com`.
For server-side work (creating events, reading orders, registering webhooks)
you use an **organisation API key** (`tlak_...`) with the Backstage API; see
[Keys and authentication](https://docs.ticketlayer.com/build/keys-and-authentication).
## What happens when someone buys
1. The page loads the Live SDK, which creates nothing until the customer acts:
browsing uses the publishable key alone.
2. The customer picks a date and tickets in the buy modal; the SDK creates a
Live session and a cart (a timed reservation) on the first add.
3. Checkout opens the hosted box office in an iframe modal on your page. The
iframe inherits the session, so it sees the same cart, and takes payment on
its own origin.
4. On success the SDK emits `checkout:completed` with the order; the modal
closes, and you can send the customer to a page with
`` (it reads `?orderId=` from the URL) or show
`` inline.
5. Your server hears about it through [webhooks](https://docs.ticketlayer.com/build/webhooks)
(`order.confirmed`, `ticket.issued`).
## Keep going
- [HTML quickstart](https://docs.ticketlayer.com/build/quickstarts/html), [Next.js quickstart](https://docs.ticketlayer.com/build/quickstarts/nextjs), [Vite + React quickstart](https://docs.ticketlayer.com/build/quickstarts/vite-react)
- [Elements reference](https://docs.ticketlayer.com/reference/elements/): every tag, attribute, event and theme token
- [Live API](https://docs.ticketlayer.com/api/live/) and [Backstage API](https://docs.ticketlayer.com/api/backstage/) references, and the OpenAPI documents at [/openapi/live.json](https://docs.ticketlayer.com/openapi/live.json) and [/openapi/backstage.json](https://docs.ticketlayer.com/openapi/backstage.json)
- [Connect an AI agent](https://docs.ticketlayer.com/build/mcp) to manage events from Claude Code, Cursor or Claude Desktop
- Building with an AI tool? Point it at [/llms.txt](https://docs.ticketlayer.com/llms.txt).
---
# HTML quickstart
Source: https://docs.ticketlayer.com/build/quickstarts/html
One file, no build tools. Works the same inside Webflow, Framer, Squarespace
or any site builder that lets you add a script tag and custom HTML.
## What you need
- A publishable key (`tlpk_...`) for a sales channel. See
[Get a key](https://docs.ticketlayer.com/build/sell-on-your-own-site#3-get-a-key).
- At least one event listed on that channel (in Backstage: the event's
listing on the channel, with an occurrence and a price scheme on sale).
## The page
```html
Tickets
What's on
Featured
```
And `thanks.html`, with the same script tag in its head:
```html
```
## What each line does
| Piece | Behaviour |
|---|---|
| The script tag | Boots a Live client from its `data-*` attributes, publishes it on `window.Ticketlayer` for the elements, injects the Elements bundle, installs the buy modal and the hosted checkout modal. Idempotent; a no-op without a key. |
| `` | Fetches the channel's listings and renders a card per event. Attributes: `columns`, `limit`, `category`. Event: `tlEventClick` with `{ eventId }`. |
| `` | Emits `tlClick` and opens the buy modal (date, then tickets) for `event-id`. Attributes include `occurrence-id`, `label`, `price`, `state`, `variant`, `size`. |
| `` and `` | The badge shows the item count and opens the drawer; the drawer holds `` with a Checkout button that opens the hosted checkout. |
| `checkout:completed` | Emitted on the Live client when the checkout iframe reports success. The payload is the order (`id`, `orderNumber`). |
| `` | Order reference, lines, totals and the tickets with QR codes and PDF links. Finds the order from `order-id`, then `?orderId=` in the URL, then the `checkout:completed` event. |
## Next
- [Elements reference](https://docs.ticketlayer.com/reference/elements/) for every attribute and event
- [Theming](https://docs.ticketlayer.com/reference/elements/#theming): the `--tl-*` tokens
- [Webhooks](https://docs.ticketlayer.com/build/webhooks) to hear about orders on your server
---
# Next.js quickstart
Source: https://docs.ticketlayer.com/build/quickstarts/nextjs
Two halves. On the **server**, `@ticketlayer/live/server` reads listings, event
detail and the theme with plain `fetch`, no DOM, no session, so your pages
render with real data and cache however Next.js caches. In the **browser**,
`@ticketlayer/live` plus the Elements handle the buy flow, the cart and the
hosted checkout.
## Install
```bash
npm install @ticketlayer/live @ticketlayer/elements @ticketlayer/elements-react
```
Note (The same shape the hosted box office uses):
These three packages are on npm, and the code below is what the hosted box
office - itself a Next.js app - does. To try an unreleased build, check out
`ticketlayer/live` (`pnpm install && pnpm build`) and link the packages in.
## Environment
```bash title=".env.local"
LIVE_API_INTERNAL_URL=https://live.ticketlayer.com
NEXT_PUBLIC_LIVE_API_URL=https://live.ticketlayer.com
NEXT_PUBLIC_CHECKOUT_BASE=https://checkout.ticketlayer.com
LIVE_PUBLISHABLE_KEY=tlpk_...
NEXT_PUBLIC_LIVE_PUBLISHABLE_KEY=tlpk_...
```
The publishable key is public by design, so it can be in both a server and a
`NEXT_PUBLIC_` variable. Never put an organisation API key (`tlak_`) in a
`NEXT_PUBLIC_` variable.
## Server: the catalogue client
```ts title="lib/live.ts"
import { createServerClient } from '@ticketlayer/live/server';
export const live = createServerClient({
apiUrl: process.env.LIVE_API_INTERNAL_URL!,
publishableKey: process.env.LIVE_PUBLISHABLE_KEY!,
// Let Next's fetch cache own revalidation instead of the SDK's TTL cache.
cache: false,
});
```
```tsx title="app/page.tsx"
import Link from 'next/link';
import { live } from '@/lib/live';
export default async function HomePage() {
const listings = await live.events.list({
fetchInit: { next: { revalidate: 300, tags: ['events'] } },
});
return (
);
} catch (err) {
if (err instanceof LiveAPIError && err.status === 404) notFound();
throw err;
}
}
```
The server client's surface: `events.list()`, `events.get(id)`, `theme.get()`,
`session.create()` (a Live session, for a WebView checkout), `get(path)` for
any other Live GET, and `clearCache()`. Every call takes `{ fetchInit }` to
pass framework fetch options through.
## Browser: boot the SDK and register Elements once
```tsx title="components/TicketlayerProvider.tsx"
'use client';
import { useEffect } from 'react';
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements } from '@ticketlayer/elements-react';
export function TicketlayerProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
// Register the custom elements after hydration (avoids SSR mismatches).
defineCustomElements();
createLiveClient({
baseUrl: process.env.NEXT_PUBLIC_LIVE_API_URL!,
publishableKey: process.env.NEXT_PUBLIC_LIVE_PUBLISHABLE_KEY!,
// Third-party pages open the hosted checkout in an iframe modal.
checkout: { mode: 'modal', embedBaseUrl: process.env.NEXT_PUBLIC_CHECKOUT_BASE! },
}).then((tl) => {
tl.on('checkout:completed', (order: { id: string }) => {
window.location.href = `/thanks?orderId=${encodeURIComponent(order.id)}`;
});
});
}, []);
return <>{children}>;
}
```
Mount it in `app/layout.tsx` around `{children}`. `createLiveClient` publishes
the client on `window.Ticketlayer`, which is how every Element finds it; there
is no prop to pass.
```tsx title="components/BuyButton.tsx"
'use client';
import { TlBuyTicketsButton } from '@ticketlayer/elements-react';
export function BuyButton({ eventId }: { eventId: string }) {
return console.log('opening buy flow')} />;
}
```
```tsx title="app/thanks/page.tsx"
'use client';
import { TlOrderConfirmation } from '@ticketlayer/elements-react';
// reads ?orderId= from the URL itself.
export default function ThanksPage() {
return ;
}
```
Elements are browser-only: every component that renders one needs
`'use client'`. Server components should render data from the server client
and hand off to a client component for anything interactive.
## Next
- [Elements reference](https://docs.ticketlayer.com/reference/elements/): `TlEventList`, `TlCartBadge`, `TlCartDrawer`, `TlMyOrders` and the rest
- [Live SDK reference](https://docs.ticketlayer.com/reference/sdk/live) for `createLiveClient` options and the `TicketlayerLive` facade
- [Webhooks](https://docs.ticketlayer.com/build/webhooks) to update your own database when an order confirms
---
# Vite + React quickstart
Source: https://docs.ticketlayer.com/build/quickstarts/vite-react
A client-only React app: no server, no backend, no database. The publishable
key is public, the cart lives in the Live session, and payment happens in the
hosted checkout, so a static bundle is all you deploy.
## Install
```bash
npm create vite@latest my-tickets -- --template react-ts
cd my-tickets
npm install @ticketlayer/live @ticketlayer/elements @ticketlayer/elements-react
```
```bash title=".env"
VITE_LIVE_API_URL=https://live.ticketlayer.com
VITE_CHECKOUT_BASE=https://checkout.ticketlayer.com
VITE_TL_PUBLISHABLE_KEY=tlpk_...
```
Note (No build step? Use the script tag):
`@ticketlayer/live`, `@ticketlayer/elements` and `@ticketlayer/elements-react`
are on npm, so the install below works. The section
[Without the npm packages](#without-the-npm-packages) is the same app built on
the CDN loader instead, for a page with no bundler.
## Boot once
```tsx title="src/main.tsx"
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements } from '@ticketlayer/elements-react';
import App from './App';
defineCustomElements();
createLiveClient({
baseUrl: import.meta.env.VITE_LIVE_API_URL,
publishableKey: import.meta.env.VITE_TL_PUBLISHABLE_KEY,
checkout: { mode: 'modal', embedBaseUrl: import.meta.env.VITE_CHECKOUT_BASE },
}).then((tl) => {
tl.on('checkout:completed', (order: { id: string }) => {
window.location.href = `/thanks?orderId=${encodeURIComponent(order.id)}`;
});
});
ReactDOM.createRoot(document.getElementById('root')!).render(
,
);
```
## Use the elements
```tsx title="src/App.tsx"
import { useState } from 'react';
import {
TlEventList,
TlBuyTicketsButton,
TlCartBadge,
TlCartDrawer,
TlOrderConfirmation,
} from '@ticketlayer/elements-react';
export default function App() {
const [eventId, setEventId] = useState(null);
const orderId = new URLSearchParams(window.location.search).get('orderId');
if (orderId) return ; // reads ?orderId= itself
return (
);
}
```
Every wrapper renders the matching custom element and turns each `tl*` event
into an `onTl*` prop with the `CustomEvent` as its argument. Attributes are
camelCased props (`eventId`, `occurrenceId`, `fullWidth`).
## Without the npm packages
Load the CDN build from `index.html` and write the tags directly. This is what
works today and is also the smallest thing an AI tool can generate correctly.
```html title="index.html"
```
```tsx title="src/App.tsx"
import { useEffect, useRef } from 'react';
export default function App() {
const list = useRef(null);
useEffect(() => {
const el = list.current;
const onClick = (e: Event) => console.log((e as CustomEvent<{ eventId: string }>).detail.eventId);
el?.addEventListener('tlEventClick', onClick);
return () => el?.removeEventListener('tlEventClick', onClick);
}, []);
return (
<>
>
);
}
```
TypeScript needs to know the tags exist:
```ts title="src/tl-elements.d.ts"
import type { DetailedHTMLProps, HTMLAttributes } from 'react';
type TlProps = DetailedHTMLProps, HTMLElement> & Record;
declare module 'react' {
namespace JSX {
interface IntrinsicElements {
'tl-event-list': TlProps;
'tl-buy-tickets-button': TlProps;
'tl-cart-badge': TlProps;
'tl-cart-drawer': TlProps;
'tl-order-confirmation': TlProps;
}
}
}
```
Attributes on raw tags are strings (`columns="3"`), and events are attached
with `addEventListener` because React does not bind custom events on
intrinsic elements. The `checkout:completed` hook is the same as in the HTML
quickstart: `window.ticketlayer.ready.then((tl) => tl.on('checkout:completed', ...))`.
## Next
- [Elements reference](https://docs.ticketlayer.com/reference/elements/) for every wrapper, prop and event
- [Theming](https://docs.ticketlayer.com/reference/elements/#theming): set `--tl-*` tokens in your CSS
- [Webhooks](https://docs.ticketlayer.com/build/webhooks) if you later add a server
---
# Keys and authentication
Source: https://docs.ticketlayer.com/build/keys-and-authentication
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](https://docs.ticketlayer.com/api/live/) | `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](https://docs.ticketlayer.com/api/live/): 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: ` |
| Staff access token (high trust) | JWT | Backstage, staff tools, MCP agents | `Authorization: Bearer ` plus `X-Ticketlayer-Org: ` |
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](https://docs.ticketlayer.com/build/concepts/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.
```html
```
```ts
import { createLiveClient } from '@ticketlayer/live';
const tl = await createLiveClient({ baseUrl: 'https://live.ticketlayer.com', publishableKey: 'tlpk_...' });
```
Calling the Live API directly:
```bash
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; `` 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](https://docs.ticketlayer.com/build/sell-on-your-own-site#3-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:
```bash
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](https://docs.ticketlayer.com/build/concepts/how-sales-work/partner-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.
```bash
curl https://api.ticketlayer.com/v1/events \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug"
```
With the SDK:
```ts
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: `: `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](https://docs.ticketlayer.com/build/sdks/typescript).
## 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](https://docs.ticketlayer.com/build/mcp)'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 ` with
`X-Ticketlayer-Org: `. 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
| | 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](https://docs.ticketlayer.com/api/backstage/versions).
---
# How sales work
Source: https://docs.ticketlayer.com/build/concepts/how-sales-work
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
| Question | Answered by | Never answered by |
|---|---|---|
| Who is calling, and how much do we trust them? | the credential | the request body |
| Under what commercial rules are they selling? | the sales channel | the model, or a path segment the caller picks |
| Which buyer is this for? | the customer token, carried on top | the 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.
| Class | Credential | Where it may be held |
|---|---|---|
| Low trust | The 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 trust | The 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 trust | The staff JWT (the identity issuer), the organisation API key `tlak_`, the device JWT, the embed session and the service JWT | Backstage, 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](https://docs.ticketlayer.com/build/concepts/how-sales-work/partner-storefront).
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`](https://docs.ticketlayer.com/api/backstage/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: `.
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:
| State | The question | Where it is asked |
|---|---|---|
| **Discoverable** | Does it appear in a list? | `GET /events` with a channel, `GET /sales/listings`, event cards |
| **Visible** | Can it be opened by id? | `GET /events/{id}` and its occurrences, prices and layout, `GET /sales/events/{id}` |
| **Sellable** | Will 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:
```json
{ "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](https://docs.ticketlayer.com/build/concepts/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](https://docs.ticketlayer.com/api/live/),
reached only with a channel key. Every response is a declared view.
- **The high-trust surface** is the staff endpoints of the
[Backstage API](https://docs.ticketlayer.com/api/backstage/), 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
| Who | Credential that authenticates | Where the channel comes from | Customer |
|---|---|---|---|
| [A buyer on your website](https://docs.ticketlayer.com/build/concepts/how-sales-work/website) | `tlpk_`, held by Live | the key | magic link; the token stays in the Live session |
| [A buyer on the hosted box office](https://docs.ticketlayer.com/build/concepts/how-sales-work/hosted-box-office) | `tlpk_`, held by Live | the storefront's `Host` | magic link; the token stays in the Live session |
| [A buyer in a native app](https://docs.ticketlayer.com/build/concepts/how-sales-work/native-app) | `tlpk_`, in the app binary | the key | magic link; the token stays in the Live session |
| [A partner's own storefront](https://docs.ticketlayer.com/build/concepts/how-sales-work/partner-storefront) | `tlsk_`, on the partner's server | the key | a partner identity assertion signed by the partner's IdP |
| [Staff at a box office or a till](https://docs.ticketlayer.com/build/concepts/how-sales-work/box-office-staff) | staff JWT | `X-Ticketlayer-Channel` | usually none; "on behalf of" is not authentication |
| [A promoter](https://docs.ticketlayer.com/build/concepts/how-sales-work/promoter) | staff JWT | `X-Ticketlayer-Channel` | usually none |
| [An outlet or reseller](https://docs.ticketlayer.com/build/concepts/how-sales-work/outlet) | staff JWT and `tlsk_`, one account | header and key, same channel | usually none; the sale posts a receivable |
| [A kiosk](https://docs.ticketlayer.com/build/concepts/how-sales-work/kiosk) | device JWT | `X-Ticketlayer-Channel` | magic link on screen, or none |
[Keys and authentication](https://docs.ticketlayer.com/build/keys-and-authentication) is the
reference for the headers each credential uses.
---
# A partner's own server-side storefront
Source: https://docs.ticketlayer.com/build/concepts/how-sales-work/partner-storefront
A partner runs their own site with their own users and wants to sell your
tickets from it: a WordPress plugin, an application behind Auth0, a custom
storefront. The server talks to the [Live API](https://docs.ticketlayer.com/api/live/), exactly as a
page or an app does, only with a stronger key. The Backstage API serves the
same operations to Live alone, as its upstream, so Live is always in the
path.
**The credential.** The partner's server holds the channel's **secret key**
(`tlsk_`, medium trust), issued for one channel and never sent to a browser.
The **channel is the key's**, so nothing names it. To sign a buyer in, the
partner's own identity provider signs a short-lived **partner identity
assertion**, which Live exchanges for a **customer token** and keeps in the
server's Live session. Both are carried on top of the secret key; neither
authenticates a request.
## The four moments
| Moment | What happens | Credential |
|---|---|---|
| List what is on sale | `GET /sales/listings` to Live under the secret key returns what the channel sells, inside its sale windows, at the channel's resolved prices. | `tlsk_`; the channel is the key's |
| Make an order | `POST /sales/carts`, then `POST /sales/carts/{id}/items`, `POST /sales/carts/{id}/checkout`, then `POST /sales/orders/{id}/payments`. The server starts a Live session with its key (`POST /session` with `{ channelKey }`) and sends the session id back as `X-Live-Session`. | `tlsk_`, plus the session id |
| Authenticate a customer | The partner's IdP signs a JWT carrying the [claims below](#the-partner-identity-assertion). `POST /sales/customer-tokens` with `{ type: "partner_assertion", assertion }` verifies it against the channel's configured issuer, public key and algorithm, and Live keeps the customer token in the session. | `tlsk_` authenticates; the assertion, then the customer token, are carried on top |
| Read the order back | `GET /sales/my-orders` under the escalated session, or `GET /sales/orders/{id}` by id under the secret key, scoped to its channel. | `tlsk_`, plus the customer token carried on top |
## The partner identity assertion
The partner never asks the buyer to log in to Ticketlayer. The channel
carries a `customerFacing.partnerIdentity` block - an `issuer`, a
`publicKeyPem` and an `algorithm` - set when the channel is configured. The
partner's identity provider signs a JWT with its private key:
```json
{
"iss": "https://partner.example.com",
"aud": "sch_...",
"iat": 1780000000,
"exp": 1780000300,
"jti": "5f0c1b9e-3a57-4d8e-9c1a-2b7f6e4d8a10",
"email": "buyer@example.com",
"sub": "member-48213"
}
```
| Claim | Required | What it must be |
|---|---|---|
| `iss` | Yes | The channel's partner identity issuer, `customerFacing.partnerIdentity.issuer`. |
| `aud` | Yes | The id of the channel whose key exchanges it, `sch_...`, not its slug. An assertion minted for one channel is refused on another, even one that trusts the same key. |
| `iat` | Yes | When it was signed. An assertion more than five minutes old is refused, and so is one past its `exp` if it carries one. Mint it immediately before the exchange. |
| `jti` | Yes | A unique id. Each one is accepted once per channel, so a new assertion is minted for every exchange. |
| `email` | Yes | The buyer's email. |
| `sub` | No | The partner's own stable id for the buyer. It is stored as the customer's `externalId` and matched before the email. |
String claims are at most 255 characters. The server exchanges the assertion
with the [customer tokens operation](https://docs.ticketlayer.com/api/live/sales-customer-tokens/createsalescustomertoken),
sending its Live session id:
```bash
curl https://live.ticketlayer.com/sales/customer-tokens \
-H "Authorization: Bearer tlsk_..." \
-H "X-Live-Session: " \
-H "Content-Type: application/json" \
-d '{"proof":{"type":"partner_assertion","assertion":""}}'
```
The customer token is bound to this channel, good for thirty minutes with a
24 hour absolute ceiling that a refresh carries forward, and Live keeps it in
the session. Identity is exchanged, never asserted: a body that names an
email without a verified assertion is refused. Auth0, a WordPress session and
a bespoke login all work the same way, because the only thing the API needs
is a signature it can verify against the key the channel was configured with.
**Matching the buyer.** With a `sub`, the customer who already holds that
external id is the buyer. Without a match, the email decides: a customer with
that email and no external id adopts the `sub`, and one with that email under
a **different** external id is another of your members, so the exchange is
refused with `409` [`PARTNER_ASSERTION_EMAIL_CONFLICT`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_EMAIL_CONFLICT)
rather than logging one member in as the other. Two members sharing one
address is the usual cause; give each their own email, or correct the
external id you assert.
**When the exchange is refused.** Each refusal names what to fix:
| Status | Code | Why |
|---|---|---|
| `401` | [`INVALID_PARTNER_ASSERTION`](https://docs.ticketlayer.com/error/INVALID_PARTNER_ASSERTION) | Not a JWT, not signed by the channel's key with its algorithm, or `iss` is not the channel's issuer. |
| `401` | [`PARTNER_ASSERTION_AUDIENCE_MISMATCH`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_AUDIENCE_MISMATCH) | `aud` is missing or names another channel. |
| `401` | [`PARTNER_ASSERTION_EXPIRED`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_EXPIRED) | More than five minutes since `iat`, or past `exp`. |
| `401` | [`PARTNER_ASSERTION_REPLAYED`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_REPLAYED) | This `jti` has already been exchanged on this channel. |
| `401` | [`PARTNER_ASSERTION_CLAIM_INVALID`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_CLAIM_INVALID) | A required claim is absent or malformed; the message names it. |
| `409` | [`PARTNER_ASSERTION_EMAIL_CONFLICT`](https://docs.ticketlayer.com/error/PARTNER_ASSERTION_EMAIL_CONFLICT) | The email belongs to a customer with a different external id. |
## Why a secret key and not an organisation API key
A secret key reaches what a storefront needs and nothing else: the
channel-scoped reads, carts, checkout, payments, the channel's own orders
(`GET /sales/orders/{id}` for another channel's order is a `404`, so existence
does not leak) and the customer records it creates (`POST /sales/customers`,
`GET` and `PATCH /sales/customers/{id}`) - never the
whole customer directory, never a catalogue mutation, never keys, reports,
the ledger or billing. That ceiling is chosen so that the full domain shapes
it receives are safe to hand to a partner. An
[organisation API key](https://docs.ticketlayer.com/build/keys-and-authentication#organisation-api-keys-server)
is for your own server administering your own organisation.
The two channel keys differ by **where they may be held**: a publishable key
may sit in a browser, a secret key may not. Both are issued with
[Issue channel key](https://docs.ticketlayer.com/api/backstage/keys/issuesaleschannelkey) and shown once.
---
# Offers
Source: https://docs.ticketlayer.com/build/concepts/how-sales-work/offers
An **offer** is one thing a buyer can pick on one date: a seating category
and a ticket type, on an occurrence, on a sales channel. "Stalls, Adult, on
Friday's performance, on the website" is an offer. The Backstage API answers
every question a page asks about offers from one route:
```
GET /v1/events/{eventId}/offers
```
It returns each offer with its price, fees included, and whether this caller
can buy it now, and why not when they cannot. Carts and checkout price through
the same engine, so the `total` an offer shows is what checkout charges for
that ticket, and a `quote` for two tickets is what checkout charges for a cart
holding those two. Show a price from here, not from a price scheme: a scheme
holds face values, and a buyer pays fees on top.
The operation reference is [Get event offers](https://docs.ticketlayer.com/api/backstage/event-offers/geteventoffers),
and `@ticketlayer/backstage` has it as `client.events(eventId).offers.get()`.
## The channel is required
Prices, fees and sale windows belong to a sales channel. The same ticket can
cost more on a partner's site than on your own, a channel's fee profile adds
its own booking fee, and a presale can open on one channel before another. So
an offer only has an answer on a channel, and the route requires one:
- send `X-Ticketlayer-Channel` with the channel's slug, or
- use a credential that names its own channel, in which case the header, if
sent, must agree with it.
Without a channel the answer is 400 [`CHANNEL_REQUIRED`](https://docs.ticketlayer.com/error/CHANNEL_REQUIRED).
An event the channel does not show to this caller is 404, the same as reading
it on that channel.
The caller needs `events.read` on the event's account. An organisation API
key (`tlak_`) with the events read scope or a staff token will do.
## A worked example
The event "The Tempest" has one performance, one category (Stalls) and two
ticket types. The price scheme says Adult is 20.00 and Concession 15.00, and
the `website` channel has its own Adult price of 22.00. The channel's fee
profile adds a 10% service fee per ticket, and the account charges a 1.25
order fee once per order.
```bash
curl "https://api.ticketlayer.com/v1/events/evt_8f2kQ/offers?quantity=2" \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug" \
-H "X-Ticketlayer-Channel: website"
```
```ts
const offers = await backstage.events('evt_8f2kQ').offers.get({ quantity: '2' });
```
The Adult offer comes back like this (one offer shown, JSend envelope
omitted):
```json
{
"event": { "id": "evt_8f2kQ", "name": "The Tempest", "timezone": "Europe/London" },
"channel": { "id": "sch_web", "slug": "website", "name": "Website" },
"context": { "at": "2026-10-01T09:00:00.000Z", "view": "buyer", "customerId": null, "code": null, "codeApplied": false, "quantity": 2, "cartId": null },
"summary": { "state": "on_sale", "currency": "GBP", "from": 1775, "to": 2545 },
"limits": { "minPerOrder": 1, "maxPerOrder": 10, "maxOrderValue": 1000000 },
"occurrences": [
{
"id": "occ_1",
"startsAt": "2026-10-09T18:30:00.000Z",
"endsAt": "2026-10-09T21:00:00.000Z",
"timezone": "Europe/London",
"status": "on_sale",
"availability": { "state": "on_sale", "reason": null, "opensAt": null, "closesAt": "2026-10-09T18:00:00.000Z" },
"offers": [
{
"categoryId": "cat_stalls",
"categoryName": "Stalls",
"ticketTypeId": "tt_adult",
"ticketTypeName": "Adult",
"description": null,
"price": {
"currency": "GBP",
"faceValue": 2200,
"discounts": [],
"fees": [
{ "code": "service", "label": "Service fee", "basis": "per_ticket", "rate": 0.1, "amount": 220, "source": { "layer": "channel_profile", "id": "fpr_web" } },
{ "code": "order", "label": "Order fee", "basis": "per_order", "rate": null, "amount": 125, "source": { "layer": "account_config", "id": "acc_main" } }
],
"total": 2545,
"source": { "layer": "channel", "schemeId": "ps_main", "level": 0, "inForceSchemeId": "ps_main" }
},
"unlock": null,
"availability": { "state": "on_sale", "reason": null, "opensAt": null, "closesAt": "2026-10-09T18:00:00.000Z" },
"remaining": { "bucket": "limited", "exact": null },
"limits": { "minPerOrder": 1, "maxPerOrder": 10, "maxPerOrderRemaining": 10 },
"quote": { "quantity": 2, "faceValue": 4400, "discounts": 0, "fees": 565, "total": 4965 }
}
]
}
]
}
```
Reading it:
- **`faceValue` is 2200**, the website's own Adult price. `price.source.layer`
is `channel` because the channel has a price for this cell; the Concession
offer, which has none, says `default` and prices at the scheme's 1500.
`level` is 0 because the price is in the scheme in force; 1 would mean it
was inherited from that scheme's parent.
- **`fees` lists every fee on buying one ticket.** The service fee is 10% of
2200 (`rate` 0.1, `amount` 220). The order fee is `per_order`: it is charged
once whatever the quantity, and it is in the single-ticket `total` because
one ticket bought alone pays it. Each fee's `source` names the configuration
that charged it: `organisation_config`, `account_config`, `channel_profile`,
`profile_rule` or `code_waiver`.
- **`total` is 2545**: `faceValue - discounts + fees`, what one ticket costs.
Money is always integer minor units in `currency`.
- **`quote` prices `quantity`** (2 here): face value 4400, fees 440 per ticket
plus the 125 order fee once, 565, for 4965. A quote is present only when the
offer is on sale.
- **`summary`** is the event on this channel in one line: the best state any
offer is in, and the cheapest and dearest `total` among the offers this
buyer can pay for now (or, when none is on sale, among those that open
later). Concession's total is 1500 + 150 + 125 = 1775.
## Availability
Every occurrence and every offer carries `availability`: a `state`, a
`reason` when it is not on sale, and the times that matter to it.
| State | Means | Carries |
|---|---|---|
| `on_sale` | This caller can buy it now | `closesAt`: when the sale they are under closes, or null when it does not |
| `not_yet_on_sale` | A sale window opens later | `opensAt`: when it opens for this caller |
| `sold_out` | Nothing left | |
| `off_sale` | Not on sale, and no window opens | `closesAt`: when the sale closed, where one did |
| `hidden` | The channel does not show it to this caller | only in the staff view (below) |
| `locked` | Reserved in the schema | see [Offers that need a tag](#offers-that-need-a-tag) |
The state of an occurrence is the same for every offer on it (the event is
not live, the performance is cancelled, the sale window has not opened); an
offer can then be worse than its occurrence, for example `sold_out` while
other categories on the same date are still `on_sale`.
A buyer is told one of four coarse reasons, which are safe to show on a page:
| Reason | State | Tell the buyer |
|---|---|---|
| `SALE_NOT_OPEN` | `not_yet_on_sale` | On sale from `opensAt` |
| `SOLD_OUT` | `sold_out` | Sold out |
| `SALE_CLOSED` | `off_sale` | Sales have closed |
| `NOT_AVAILABLE` | `off_sale` | Not available |
With `view=staff`, the reason is the fine one instead, which is what a box
office or a support tool needs to fix the event: `EVENT_NOT_LIVE`,
`OCCURRENCE_DRAFT`, `OCCURRENCE_CANCELLED`, `OCCURRENCE_COMPLETED`,
`NO_PRICE_SCHEME`, `PRICE_SCHEME_NOT_ACTIVE`, `NO_PRICE_FOR_TYPE`,
`NO_INVENTORY`, `OCCURRENCE_SOLD_OUT`, `NO_CAPACITY`, `NOT_LISTED` and
`NOT_VISIBLE`, alongside `SALE_NOT_OPEN` and `SALE_CLOSED`.
## Remaining capacity
`remaining.bucket` says how much of the offer's category is left, in words a
page can show without publishing a count:
| Bucket | Means |
|---|---|
| `available` | More than 20% of the category's capacity, and more than 10 tickets |
| `limited` | 20% of the capacity or less |
| `few` | 10 tickets or fewer |
| `none` | Nothing left |
`bucket` is null when the category has no inventory. `remaining.exact`, the
count itself, is filled in only with `view=staff` for a caller holding
`inventory.read`; everywhere else it is null.
## Limits
`limits` on the response are the order limits checkout enforces:
`minPerOrder`, `maxPerOrder` (10 unless the organisation's order
configuration sets another) and `maxOrderValue`, in minor units. Each offer
repeats `minPerOrder` and `maxPerOrder` in its own `limits`. Pass
`cartId`, an open cart on the same channel, and each offer's
`maxPerOrderRemaining` is `maxPerOrder` less what that cart already holds, so a
quantity picker can stop at the right number. It is 0 on a sold-out offer.
## The buyer view and the staff view
`view=buyer` is the default: what a member of the public on the channel is
told. It lists the occurrences a buyer on the channel sees (on sale or sold
out), the coarse reasons, the bucket without a count, and no `unlock`.
`view=staff` lists every occurrence, with the fine reasons and, for a caller
holding `inventory.read`, `remaining.exact`. It also prices an occurrence
whose scheme is not active yet, so the box office can see what a draft says
and why it is off sale. A listing that is switched on but not visible right
now (a link-only listing outside its window, say) is 404 in the buyer view;
in the staff view, for a caller holding `events.list_hidden` on the channel,
it is returned with the state `hidden` and the reason `NOT_VISIBLE`.
## Pricing for one customer, a code, an instant
- **`customerId`** prices for that customer: their [customer tags](https://docs.ticketlayer.com/build/concepts/tags)
are read server-side and admit them to what those tags gate. It needs
`customers.read` on the customer.
- **`code`** previews what a presale code would unlock. It is never redeemed
and no use is counted; `context.codeApplied` says whether it unlocks
anything now.
- **`at`** evaluates at another instant (ISO 8601), for example to check what
a page will show when a window opens.
- **`occurrenceId`** narrows the response to one date.
## Offers that need a tag
A ticket type can require a customer tag, for example a members' price. An
offer of that ticket type is returned only to a caller who holds the tag,
because they are the customer named by `customerId` or because the code
previewed grants it; `unlock.source` then names the tag in the staff view.
For anyone else the offer is left out of the response, so a public page never
shows a price its reader cannot pay. The schema also declares a `locked` state
with the buyer reason `TAG_REQUIRED`; the engine does not return it, and a
gated offer is always left out rather than returned as `locked`.
## Offers and the event read
For a card or a list, where one line per event is enough, read the events with
`include=prices` on a channel instead of calling offers once per event. Each
event's `prices` is the same summary the engine computes: `from` and `to`
(fees included, `feesIncluded: true`), `currency`, `state` and `sellable`. See
[Syncing events](https://docs.ticketlayer.com/build/webhooks/sync-events).
---
# Tags
Source: https://docs.ticketlayer.com/build/concepts/tags
A **tag** is a short, stable label, such as `member` or `student`, that the
rest of the platform refers to by its slug. Tags do two jobs:
- **Customer tags** say who a buyer is. A ticket type can require one (a
members' price), and a sale window can be open only to holders of one (a
members' priority booking). They are lasting and belong to the customer,
not to a channel: a member is a member on the website, in the box office
and on a partner's site.
- **Descriptive tags** say what something is: an event (`family`), a venue
(`accessible`), a ticket type (`concession`) or a price (`early-bird`).
Ticket-type and price tags are what an offer's fee rules match on, which is
why the offers engine reads them.
Every tag an organisation uses is **defined** once. The definition fixes the
slug, gives it a label a buyer reads, and says what it may be applied to and
who may grant it.
## Tag definitions
`/v1/tag-definitions` holds them. `@ticketlayer/backstage` has the same
operations as `client.tags.list()`, `create()`, `get()`, `update()` and
`archive()`.
```bash
curl -X POST https://api.ticketlayer.com/v1/tag-definitions \
-H "Authorization: Bearer " \
-H "X-Ticketlayer-Org: your-org-slug" \
-H "Content-Type: application/json" \
-d '{
"slug": "member",
"label": "Members",
"description": "Current club members, renewed each season",
"kind": "customer",
"grantableBy": ["staff", "api"]
}'
```
| Field | Means |
|---|---|
| `slug` | The key every reference stores. Lower case letters and digits, separated by `.` or `-` (`member`, `member.priority-booking`). Unique in the organisation, and fixed once created: a slug in use, archived or not, is 409 [`TAG_SLUG_TAKEN`](https://docs.ticketlayer.com/error/TAG_SLUG_TAKEN). |
| `label` | What a buyer reads. Renaming it changes what buyers see and no reference. |
| `kind` | `customer`, `event`, `venue`, `ticket_type` or `price`. Fixed once created. |
| `appliesTo` | What the tag may be put on: any of `customers`, `events`, `venues`, `ticket_types` and `prices`. It must include the kind's own target, which is the default. |
| `grantableBy` | Who may grant the tag to a customer (below). Empty on a tag that does not apply to customers. |
| `autoDefined` | True when the platform defined the tag rather than a person: a name already in use when definitions arrived, or an event or venue tag written before anyone defined it. Editing the definition makes it false. |
| `archivedAt` | Set when archived. |
Defining and editing tags needs `tags.write` over every account in the
organisation; reading them needs `tags.read`. An organisation API key can hold
the `tags.read` and `tags.write` scopes.
**Archiving** (`DELETE /v1/tag-definitions/{id}`, or `PATCH` with
`archived: true`) deletes nothing. The slug stays reserved, every reference
keeps working and customers keep the tag, but it can no longer be granted.
`PATCH` with `archived: false` restores it. Archived definitions are left out
of the list unless you pass `includeArchived=true`.
## Who may grant a customer tag
`grantableBy` lists who may give the tag to a customer:
| Grantor | Means |
|---|---|
| `staff` | A person holding the `customertags.grant` permission, in Backstage or through the API with a staff token. The default for a customer tag. |
| `api` | An organisation API key, or a delegated token, holding the `customertags.write` scope. Use it when your own system decides membership: a CRM, a membership database, a sign-up flow. |
| `code` | A code acting as the tag: redeeming a presale code grants the tag it carries. |
Adding `api` to a definition needs a person, not a key: a key that tries is
refused with 403 [`TAG_API_GRANTING_NEEDS_STAFF`](https://docs.ticketlayer.com/error/TAG_API_GRANTING_NEEDS_STAFF).
That keeps a key from widening what keys may grant.
## Customer tags
A customer's tags are at `/v1/customers/{customerId}/tags`
(`client.customers.listTags()`, `grantTag()` and `revokeTag()`).
```bash
curl -X POST https://api.ticketlayer.com/v1/customers/cus_01J.../tags \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug" \
-H "Content-Type: application/json" \
-d '{ "tag": "member", "source": "membership", "expiresAt": "2027-08-31T23:59:59Z" }'
```
A grant names the tag by slug, and may carry an `expiresAt` and `metadata`.
It is refused when:
- the slug has no definition: 400 [`TAG_NOT_DEFINED`](https://docs.ticketlayer.com/error/TAG_NOT_DEFINED);
- the definition does not apply to customers: 400 [`TAG_NOT_APPLICABLE`](https://docs.ticketlayer.com/error/TAG_NOT_APPLICABLE);
- the definition is archived: 400 [`TAG_ARCHIVED`](https://docs.ticketlayer.com/error/TAG_ARCHIVED);
- the caller is not in `grantableBy`: 403 [`TAG_NOT_GRANTABLE`](https://docs.ticketlayer.com/error/TAG_NOT_GRANTABLE);
- the customer already holds it: 409 [`CUSTOMER_TAG_ALREADY_GRANTED`](https://docs.ticketlayer.com/error/CUSTOMER_TAG_ALREADY_GRANTED).
Each grant records its `source`: `manual` (staff or an API key), `membership`,
`import`, or `presale` for a grant made by redeeming a presale code.
**Revoking** (`POST /v1/customers/{customerId}/tags/{customerTagId}/revoke`)
keeps the grant and marks it revoked. A person holding `customertags.revoke`
may revoke any tag; a key may revoke only a tag whose definition lists `api`.
The list leaves revoked grants out unless you pass `includeRevoked=true`.
A grant whose `expiresAt` has passed stops applying on its own.
## What a customer tag admits
A tag a customer holds, and has not had revoked or seen expire, is read
server-side wherever that customer buys. A request never asserts one.
- **Gated ticket types.** A ticket type that requires a tag is offered only to
a customer who holds it. See [Offers that need a tag](https://docs.ticketlayer.com/build/concepts/how-sales-work/offers#offers-that-need-a-tag).
- **Sale windows.** A listing rule's window can name `audienceTags`; any of
them admits the customer to that window, and an empty list is everyone.
This is how a members' priority booking opens a week before general sale.
## Webhooks
`customer.tag_granted` and `customer.tag_revoked` tell your system when a
customer gains or loses a tag, including when a presale code grants one and
when a grant expires (`reason: expired`). They are described in
[Webhooks](https://docs.ticketlayer.com/build/webhooks#customer-tags).
## Benefits are customer tags
Customer tags were called benefits. The benefits routes have gone; each one's
tags route acts on the same grants:
| Was | Use |
|---|---|
| `GET /v1/customers/{customerId}/benefits` | `GET /v1/customers/{customerId}/tags` |
| `POST /v1/customers/{customerId}/benefits` | `POST /v1/customers/{customerId}/tags` |
| `POST /v1/customers/{customerId}/benefits/{benefitId}/revoke` | `POST /v1/customers/{customerId}/tags/{customerTagId}/revoke` |
In the same way, a listing rule window's `audience` (one slug) is deprecated
in favour of `audienceTags` (a list), and a ticket type's `requiredBenefit`
holds the slug of the customer tag it requires.
The operations are in the API reference under
[Tags](https://docs.ticketlayer.com/api/backstage/tags) and [Customers](https://docs.ticketlayer.com/api/backstage/customers).
---
# Webhooks
Source: https://docs.ticketlayer.com/build/webhooks
Register a URL and Ticketlayer POSTs a signed JSON envelope to it whenever
one of the events below happens in your organisation. Payloads are
constructed field by field from a fixed, versioned view (never a raw database
row), so what is documented here is exactly what arrives.
## Event types
| Type | When | Payload |
|---|---|---|
| `order.created` | A new order was started | Order |
| `order.confirmed` | An order was paid and confirmed | Order |
| `order.cancelled` | An order was cancelled | Order |
| `order.refunded` | An order was refunded | Order |
| `customer.created` | A new customer was created | Customer |
| `event.created` | A new event was created | Event |
| `ticket.issued` | Tickets (passes) were issued for an order | Tickets issued |
| `ticket.redeemed` | A ticket was scanned and redeemed | Ticket redeemed |
| `event.updated` | An event's content, images, status, occurrences, prices, venue or layout changed. At most one delivery per event every 30 seconds | [Event updated](#event-updated) |
| `event.deleted` | An event was deleted | [Event deleted](#event-deleted) |
| `listing.changed` | A channel listing was created, updated or removed | [Listing changed](#listing-changed) |
| `listing.status_changed` | What the public sees of a channel listing changed, including when a sale window opens or closes with no edit | [Listing status changed](#listing-status-changed) |
| `venue.updated` | A venue was edited | [Venue updated](#venue-updated) |
| `customer.tag_granted` | A customer was granted a tag | [Customer tags](#customer-tags) |
| `customer.tag_revoked` | A customer's tag was revoked or expired | [Customer tags](#customer-tags) |
`GET /v1/webhook-event-types` returns this list with descriptions, which is
what the Backstage subscription UI shows.
## Payloads
Money is in integer minor units with an ISO 4217 `currency` (`"totalAmount": 2500,
"currency": "GBP"` is 25.00 GBP). Instants are ISO 8601 strings. Every
nullable field is present as `null` rather than omitted.
**Order** (`order.*`)
```json
{
"id": "ord_01J...",
"orderNumber": "TL-10432",
"status": "confirmed",
"currency": "GBP",
"totalAmount": 5400,
"subtotalAmount": 5000,
"customerId": "cus_01J...",
"accountId": "acc_01J...",
"createdAt": "2026-09-13T19:04:11.000Z",
"lineItemCount": 2
}
```
**Customer** (`customer.created`)
```json
{
"id": "cus_01J...",
"email": "ada@example.com",
"firstName": "Ada",
"lastName": "Lovelace",
"accountId": "acc_01J...",
"createdAt": "2026-09-13T19:04:11.000Z"
}
```
**Event** (`event.created`)
```json
{
"id": "evt_01J...",
"name": "Jazz Night",
"status": "draft",
"accountId": "acc_01J...",
"venueId": "vnu_01J...",
"timezone": "Europe/London",
"createdAt": "2026-09-13T19:04:11.000Z"
}
```
**Tickets issued** (`ticket.issued`)
```json
{
"orderId": "ord_01J...",
"accountId": "acc_01J...",
"passCount": 2,
"passes": [
{ "id": "pas_01J...", "ticketTypeId": "tkt_01J...", "occurrenceId": "occ_01J...", "status": "issued" },
{ "id": "pas_01K...", "ticketTypeId": "tkt_01J...", "occurrenceId": "occ_01J...", "status": "issued" }
]
}
```
**Ticket redeemed** (`ticket.redeemed`)
```json
{
"redemptionId": "red_01J...",
"passId": "pas_01J...",
"entitlementId": "ent_01J...",
"accountId": "acc_01J..."
}
```
## Change notifications
`event.updated`, `event.deleted`, `listing.changed`, `listing.status_changed`
and `venue.updated` are **thin**: they say what changed and carry the ids to
re-fetch, never the content itself. An event's description, dates or prices
are read from the API when the delivery arrives, so a receiver always stores
the current version, whatever order deliveries arrive in and however many
were retried. [Syncing events](https://docs.ticketlayer.com/build/webhooks/sync-events#keep-up-to-date-with-webhooks)
shows a receiver built on them.
- **Deduplicate on `Ticketlayer-Event-Id`.** Deliveries are at least once, and
the id is stable across retries and across endpoints.
- **Order `event.updated` on `data.sequence`.** It is strictly increasing per
event. Drop a delivery whose `sequence` is lower than one already processed
for that event: a later delivery has already been acted on, and the
re-fetch it caused was newer.
- **`event.updated` is coalesced.** Changes to an event within 30 seconds are
delivered once, with every area that changed in `data.changes`, so editing
ten dates is one delivery. Two deliveries for one event are never less than
30 seconds apart.
### Event updated
`event.updated`, to the event's account. `changes` is the set of areas that
changed since the previous delivery: `content`, `images`, `status` (the event
status and its listing rules), `occurrences`, `prices`, `venue` and `layout`.
It is a hint for what to re-fetch; the event itself is read from
`GET /v1/events/{id}`. `updatedAt` is the event's `contentUpdatedAt`.
`sequence` is `contentUpdatedAt` in epoch milliseconds, or one more than the
previous delivery's when that is not greater.
```json
{
"id": "evt_01J...",
"organisationId": "org_01J...",
"name": "Jazz Night",
"status": "on_sale",
"accountId": "acc_01J...",
"venueId": "vnu_01J...",
"timezone": "Europe/London",
"createdAt": "2026-09-13T19:04:11.000Z",
"updatedAt": "2026-09-20T10:15:42.120Z",
"changes": ["occurrences", "prices"],
"sequence": 1789899342120
}
```
### Event deleted
`event.deleted`, to the event's account. An `event.updated` still waiting in
its 30 second window when the event is deleted is not sent.
```json
{
"id": "evt_01J...",
"organisationId": "org_01J...",
"accountId": "acc_01J...",
"deletedAt": "2026-09-20T10:15:42.000Z"
}
```
### Listing changed
`listing.changed`, to the channel's account and the event's account (once
when they are the same): a listing's settings were saved.
`change` is `created`, `updated` or `removed`, and every save sends one, even
a save that changes nothing a buyer sees. `state` is what the public sees of
the listing after the write: `discoverable` (in the channel's list),
`visible` (can be opened), `sellable`, the next moment it becomes sellable
(`opensAt`) and the end of the window it sells under (`closesAt`).
`nextTransitionAt` is the next window boundary at which that state can change
with no edit, or null; a receiver can schedule its own re-fetch for it.
```json
{
"eventId": "evt_01J...",
"listingId": "lst_01J...",
"channel": { "id": "sch_01J...", "slug": "website" },
"organisationId": "org_01J...",
"accountId": "acc_01J...",
"change": "updated",
"state": {
"discoverable": true,
"visible": true,
"sellable": false,
"opensAt": "2026-10-01T09:00:00.000Z",
"closesAt": null
},
"nextTransitionAt": "2026-10-01T09:00:00.000Z",
"occurredAt": "2026-09-20T10:15:42.000Z"
}
```
### Listing status changed
`listing.status_changed`, to the same accounts as `listing.changed`: `discoverable`,
`visible` or `sellable` changed, and only when one did. It is sent when a
sale window opens or closes with no edit, so a page can switch to "on sale"
without polling. `previous` is the state before; `state` and
`nextTransitionAt` are as in `listing.changed`. `cause` says why:
| `cause` | Means |
|---|---|
| `scheduled` | A window opened or closed with no edit |
| `manual` | The listing rules were changed, and that changed the state now |
| `window_edited` | A window was moved, and that moved the state |
| `event_status` | The event entered or left a live status, or was deleted |
| `listed`, `unlisted` | The listing was created or removed |
```json
{
"eventId": "evt_01J...",
"listingId": "lst_01J...",
"channel": { "id": "sch_01J...", "slug": "website" },
"organisationId": "org_01J...",
"accountId": "acc_01J...",
"cause": "scheduled",
"previous": { "discoverable": true, "visible": true, "sellable": false },
"state": { "discoverable": true, "visible": true, "sellable": true, "opensAt": null, "closesAt": null },
"nextTransitionAt": null,
"occurredAt": "2026-10-01T09:00:00.000Z"
}
```
### Venue updated
`venue.updated`, to the venue's account. `changes` names the venue fields
that changed, by their API names; a save that changes none of them sends
nothing. The events at the venue that are on sale also receive
`event.updated` with `venue` in `changes`.
```json
{
"id": "vnu_01J...",
"organisationId": "org_01J...",
"accountId": "acc_01J...",
"updatedAt": "2026-09-20T10:15:42.000Z",
"changes": ["name", "streetAddress"]
}
```
## Customer tags
`customer.tag_granted` and `customer.tag_revoked` say a customer gained or
lost a [tag](https://docs.ticketlayer.com/build/concepts/tags). They go to the customer's account;
a customer the organisation's accounts share has `accountId: null` and is
delivered to every account in the organisation with an endpoint.
A grant is announced granted once and, if it ends, revoked once.
`customer.tag_revoked` carries `reason`: `revoked` when staff or an API key
took the tag away, `expired` when its `expiresAt` passed, in which case
`occurredAt` is the expiry instant. `source` is where the grant came from:
`manual`, `membership`, `import` or `presale` (a presale code redemption).
```json
{
"customerId": "cus_01J...",
"tag": "member",
"customerTagId": "cent_01J...",
"source": "membership",
"expiresAt": "2027-08-31T23:59:59.000Z",
"organisationId": "org_01J...",
"accountId": "acc_01J...",
"reason": "expired",
"occurredAt": "2027-08-31T23:59:59.000Z"
}
```
`customer.tag_granted` has the same fields without `reason`, and its
`occurredAt` is when the tag was granted.
## The delivery
Each delivery is one `POST` with a JSON body:
```json
{
"id": "whd_01J...",
"type": "order.confirmed",
"version": "2026-06-13",
"createdAt": "2026-09-13T19:04:12.000Z",
"data": { "id": "ord_01J...", "orderNumber": "TL-10432", "...": "..." }
}
```
| Header | Value |
|---|---|
| `Content-Type` | `application/json` |
| `User-Agent` | `Ticketlayer-Webhooks/1.0` |
| `Ticketlayer-Signature` | `t=,v1=` |
| `Ticketlayer-Event-Type` | The event type, same as `type` in the body |
| `Ticketlayer-Event-Id` | The id of the source event; stable across retries of the same event |
| `Ticketlayer-Delivery-Id` | The id of this delivery attempt series, same as `id` in the body |
Respond with any `2xx` within 15 seconds. Anything else, or a timeout, is
retried with backoff; the delivery is marked failed when retries are
exhausted. Deliveries and their responses are listed at
`GET /v1/webhook-deliveries` and in Backstage.
Use `Ticketlayer-Event-Id` to deduplicate: a retry carries the same value,
and so does the same event delivered to another of your endpoints.
## Payload versions
Webhook payloads are a public contract. Each endpoint pins the payload version
current when it was created (`2026-06-13` today) and keeps receiving that
shape: when a payload changes in a breaking way a new dated version is added
with a transform back to the previous one, and deliveries are transformed down
to the endpoint's pinned version before sending. The version is stamped in
every envelope as `version`.
Receiving them is a separate job: see
[Receive webhooks](https://docs.ticketlayer.com/build/webhooks/receive) for registering an
endpoint, verifying the signature and testing against a tunnel.
---
# Receive webhooks
Source: https://docs.ticketlayer.com/build/webhooks/receive
You have a URL that should hear about orders, customers, events and tickets.
This is how to register it, prove each delivery came from Ticketlayer, and
try it before an order exists. What arrives, and in what shape, is in
[Webhooks](https://docs.ticketlayer.com/build/webhooks). A receiver that keeps a copy of your
events current from `event.updated`, `event.deleted` and the listing webhooks
is in [Syncing events](https://docs.ticketlayer.com/build/webhooks/sync-events#keep-up-to-date-with-webhooks).
## Register an endpoint
With an organisation API key or a staff token:
```bash
curl -X POST https://api.ticketlayer.com/v1/webhook-endpoints \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug" \
-H "Content-Type: application/json" \
-d '{
"accountId": "acc_01J...",
"url": "https://hooks.example.com/ticketlayer",
"description": "Orders to the CRM",
"eventTypes": ["order.confirmed", "ticket.issued"]
}'
```
The response carries the endpoint and, once only, its `secret`. Omit
`eventTypes` to receive every type. Endpoints are per account (the
organisation's sub-unit that owns the events); the same URL can be registered
for several accounts.
Operations: [create](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/createwebhookendpoint),
[list](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/listwebhookendpoints),
[get](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/getwebhookendpoint),
[update](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/updatewebhookendpoint) (URL,
description, event types, `enabled`/`disabled`),
[delete](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/deletewebhookendpoint) and
[test](https://docs.ticketlayer.com/api/backstage/webhook-endpoints/testwebhookendpoint), which sends a
signed sample delivery.
## Verify the signature
The signing secret is returned once when the endpoint is created. The
signature is an HMAC-SHA256 of `"."` with that secret, hex
encoded; reject deliveries whose `t` is more than five minutes from now.
```ts
import crypto from 'node:crypto';
export function verifyTicketlayerSignature(rawBody: string, header: string, secret: string, toleranceSeconds = 300): boolean {
const parts = Object.fromEntries(header.split(',').map((kv) => kv.split('=').map((s) => s.trim())));
const t = Number(parts.t);
const v1 = parts.v1;
if (!t || !v1) return false;
if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSeconds) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const a = Buffer.from(v1, 'hex');
const b = Buffer.from(expected, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```
Compute the HMAC over the **raw** request body, before any JSON parsing or
re-serialisation.
```ts title="Express"
app.post('/webhooks/ticketlayer', express.raw({ type: 'application/json' }), (req, res) => {
const raw = req.body.toString('utf8');
if (!verifyTicketlayerSignature(raw, req.get('Ticketlayer-Signature') ?? '', process.env.TL_WEBHOOK_SECRET!)) {
return res.status(400).send('bad signature');
}
const envelope = JSON.parse(raw);
if (envelope.type === 'order.confirmed') {
// envelope.data is the Order payload above
}
res.sendStatus(204);
});
```
## Local development
Run a tunnel (for example `ngrok http 3000`) and register the tunnel URL as
the endpoint. The test operation above is the quickest way to see a
delivery without buying a ticket.
---
# Syncing events
Source: https://docs.ticketlayer.com/build/webhooks/sync-events
This guide is for a server that keeps its own copy of your events, such as a
CMS, a static site build or a WordPress plugin. The copy stays current by
reading the Backstage API's two event routes:
- `GET /v1/events`, the list
- `GET /v1/events/{id}`, one event
Every event includes the fields a public page shows: `name`, `subtitle`,
`shortDescription`, `description`, `images` (`card` and `hero`), `venueName`,
`ticketTypes` and `presentation`. It also includes `contentUpdatedAt`, which
is the field a sync depends on. `include` adds the dates, the venue and the
prices.
You need an organisation API key (`tlak_`) with the events read scope, or a
staff token, plus the `X-Ticketlayer-Org` header. See
[Keys and authentication](https://docs.ticketlayer.com/build/keys-and-authentication).
## Read what a channel lists
Send `X-Ticketlayer-Channel` with the slug of the channel your site sells
through. The list then returns what that channel lists right now, and each
event carries a `listing` block that says how the channel lists it. Without
the header you get the administrative view: every event, drafts included.
```bash
curl "https://api.ticketlayer.com/v1/events?include=occurrences,venue,prices&limit=50" \
-H "Authorization: Bearer tlak_..." \
-H "X-Ticketlayer-Org: your-org-slug" \
-H "X-Ticketlayer-Channel: website"
```
`include` is comma-separated:
| Value | Adds |
|---|---|
| `occurrences` | `occurrences`: each date's `startsAt`, `endsAt`, `timezone`, local `startDate` and `startTime`, `status`, and the occurrence's own venue when it has one. On a channel, only the dates a buyer there sees (on sale or sold out). |
| `venue` | `venue`: `id`, `name`, `streetAddress`, `city`, `country`, `coordinates`, `timezone` and `website`, or `null` when the event has no venue. |
| `prices` | `prices`: what a buyer on the channel pays for one ticket, fees included. See [Show prices](#show-prices). The list needs a channel for it, otherwise 400 [`CHANNEL_REQUIRED`](https://docs.ticketlayer.com/error/CHANNEL_REQUIRED). |
The list returns at most 50 occurrences per event. When an event has more,
`occurrencesTruncated` is `true`. For that event, read
`GET /v1/events/{id}?include=occurrences`, which returns every occurrence.
`include=venue` replaces `expand=venue`, which is deprecated. `images.card.url`
and `images.hero.url` replace `imageUrl` and `heroImageUrl`, which are also
deprecated.
By default a channel-scoped list returns only what the channel lists
(`visibility=discoverable`). `visibility=visible` also returns link-only
listings: events a buyer can open by link that are not in the list. Each of
those has `listing.discoverable: false`, so you can publish it as an unlisted
page.
## Page with a cursor
Pass `pagination.nextCursor` from each page back as `cursor` until it is
`null`. Keyset pages are stable while events are created or edited between
requests. With `page`, the same event can appear on two pages or be skipped.
```ts
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',
headers: { 'X-Ticketlayer-Channel': 'website' },
});
async function readAll(updatedSince?: string) {
const events = [];
let cursor: string | undefined;
do {
const page = await backstage.events.list({
include: ['occurrences', 'venue', 'prices'],
limit: '50',
...(updatedSince ? { updatedSince } : {}),
...(cursor ? { cursor } : {}),
});
events.push(...page.events);
cursor = page.pagination.nextCursor ?? undefined;
} while (cursor);
return events;
}
```
`include`, `updatedSince` and `cursor` are typed in `@ticketlayer/backstage`
2.1.0 and later.
## Show prices
With `include=prices` on a channel, each event carries `prices`, computed by
the [offers engine](https://docs.ticketlayer.com/build/concepts/how-sales-work/offers), the one checkout charges
through:
```json
"prices": {
"currency": "GBP",
"from": 1775,
"to": 2545,
"feesIncluded": true,
"state": "on_sale",
"sellable": true
}
```
- `from` and `to` are what a buyer pays for one ticket of the cheapest and
the dearest offer on sale, fees included, in integer minor units. When
nothing is on sale yet they cover the offers that open later, and they are
null when there are none.
- `state` is the best state any offer is in for a buyer: `on_sale`,
`not_yet_on_sale`, `sold_out`, `off_sale`, or `hidden` for a listing a buyer
cannot see. `sellable` is `true` when it is `on_sale`.
Show `from` as the price on a card ("From 17.75"). For the full breakdown on
an event page (each ticket type, its fees, what is left, the order limits),
read [the event's offers](https://docs.ticketlayer.com/api/backstage/event-offers/geteventoffers).
Prices depend on the channel: a channel can have its own price for a ticket,
and its own fees. That is why the list needs one, and why a sync reads prices
on the channel its site sells through. `GET /v1/events/{id}?include=prices`
without a channel returns `prices.byChannel` instead: the same summary for
each channel that lists the event.
`listing.priceFrom` on a channel-scoped read is the lowest face value, before
fees. A page that shows a price shows `prices.from`.
## Keep up to date with webhooks
Subscribe an endpoint to the event and listing webhooks, and the sync hears
about a change as it happens instead of on its next poll:
| Webhook | The sync |
|---|---|
| `event.updated` | Re-fetches the event on its channel. `changes` names what changed (`content`, `images`, `status`, `occurrences`, `prices`, `venue`, `layout`). |
| `event.deleted` | Deletes the event. |
| `listing.changed`, `listing.status_changed` | Re-fetches the event on its channel: the listing was edited, or a sale window opened or closed. |
`venue.updated` needs no handler of its own here: the events at the venue that
are on sale also receive `event.updated` with `venue` in `changes`. The
payloads are in [Webhooks](https://docs.ticketlayer.com/build/webhooks#change-notifications),
and registering an endpoint and verifying the signature are in
[Receive webhooks](https://docs.ticketlayer.com/build/webhooks/receive).
The payloads are thin: they say which event changed, not what it now says. A
receiver reads the event from the API, so it always stores the current
version whatever order deliveries arrive in. Two rules keep it correct under
retries and reordering:
- **Deduplicate on `Ticketlayer-Event-Id`.** Deliveries are at least once; a
retry carries the same id.
- **Order on `sequence`.** `event.updated` carries a `sequence` that is
strictly increasing per event. Keep the highest one processed for each event
and drop a delivery below it.
```ts
import express from 'express';
import { BackstageAPIError } from '@ticketlayer/backstage';
// backstage: the client from "Page with a cursor", with X-Ticketlayer-Channel set.
// verifyTicketlayerSignature: from "Receive webhooks".
app.post('/webhooks/ticketlayer', express.raw({ type: 'application/json' }), async (req, res) => {
const raw = req.body.toString('utf8');
if (!verifyTicketlayerSignature(raw, req.get('Ticketlayer-Signature') ?? '', process.env.TL_WEBHOOK_SECRET!)) {
return res.status(400).send('bad signature');
}
const deliveryEventId = req.get('Ticketlayer-Event-Id')!;
if (await alreadyProcessed(deliveryEventId)) return res.sendStatus(204);
const { type, data } = JSON.parse(raw);
switch (type) {
case 'event.updated': {
const held = await storedSequence(data.id); // null when you hold none
if (held !== null && data.sequence < held) break; // a newer delivery was processed
await refresh(data.id, data.sequence);
break;
}
case 'event.deleted':
await remove(data.id);
break;
case 'listing.changed':
case 'listing.status_changed':
if (data.channel.slug === 'website') await refresh(data.eventId, null);
break;
}
await markProcessed(deliveryEventId);
res.sendStatus(204);
});
/** Re-read the event on the channel: store it, or delete it when the channel no longer shows it. */
async function refresh(eventId: string, sequence: number | null) {
try {
const { event } = await backstage.events.get(eventId, { include: ['occurrences', 'venue', 'prices'] });
await upsert(event, sequence); // keyed by event.id; keep the higher sequence
} catch (error) {
if (error instanceof BackstageAPIError && error.statusCode === 404) {
await remove(eventId);
return;
}
throw error; // answer 5xx: the delivery is retried
}
}
```
Respond within 15 seconds. When a re-fetch can take longer, record the
delivery, answer `204` and do the work from a queue.
`event.updated` is sent at most once every 30 seconds per event, with every
area that changed in that time, so a burst of edits is one re-fetch.
## Backfill with updatedSince
Webhooks carry changes from the moment an endpoint subscribes. For the
first load, and to catch up after the receiver has been down for longer than
retries last, read with `updatedSince`.
`updatedSince` returns the events whose `contentUpdatedAt` is at or after the
instant you pass. `contentUpdatedAt` changes when anything a public page of
the event shows changes: the event, one of its occurrences, a price scheme,
its layout or its venue. An edited date or a renamed venue brings the event
back, even though the event row itself did not change.
Keep a watermark: the highest `contentUpdatedAt` you have stored. Ask for
everything since the watermark **minus a few seconds**. A change is stamped
when it is made but only becomes visible when its transaction commits. A
write in flight while you read can therefore carry a stamp just below the
highest one you saw, and without the overlap you would miss it.
```ts
const OVERLAP_MS = 5_000;
async function catchUp(watermark: string | null) {
const since = watermark
? new Date(new Date(watermark).getTime() - OVERLAP_MS).toISOString()
: undefined;
const changed = await readAll(since);
for (const event of changed) {
await upsert(event, null); // keyed by event.id
}
// The list is ordered newest-created first, not by contentUpdatedAt,
// so take the maximum over everything read.
return changed.reduce(
(max, e) => (max === null || e.contentUpdatedAt > max ? e.contentUpdatedAt : max),
watermark,
);
}
```
The overlap means an event can come back that you have already stored. Make
`upsert` idempotent by `id`, and skip the write when the stored
`contentUpdatedAt` is the same.
## Reconcile
`updatedSince` finds events whose content changed. It does not find events
that stopped being on the channel, or events that were deleted; the webhooks
above do. As a safety net for a delivery that was never processed, run a full
read without `updatedSince` now and then, for example nightly:
- delete the events you hold that are not in it
- store the ones you do not hold
The full parameter reference is under
[List events](https://docs.ticketlayer.com/api/backstage/events/listevents) and
[Get event](https://docs.ticketlayer.com/api/backstage/events/getevent).
---
# Connect an AI agent (MCP)
Source: https://docs.ticketlayer.com/build/mcp
Ticketlayer ships a **remote [MCP](https://modelcontextprotocol.io) server** so you
can point your own AI agent - Claude Desktop, Claude Code, an MCP Inspector
session, or anything that speaks MCP - at your Ticketlayer account and have it
read your catalogue, orders and customers, **query your analytics**, and (with the
right permission) create and update events.
The agent acts **as you**. Every tool call carries your own credential to the same
permissioned API the dashboard uses, so an agent can never do more than your own
role allows, and never sees another organisation's data.
| | |
|---|---|
| **Endpoint** | `https://mcp.ticketlayer.com/mcp` |
| **Transport** | Streamable HTTP |
| **Auth** | OAuth 2.1 - Dynamic Client Registration + PKCE, with the `resource` indicator |
| **Authorization server** | `https://auth.ticketlayer.com` |
| **Scopes** | One per kind of record, read or write: see [Scopes & permissions](#scopes--permissions) |
## How it works
```
your agent ──OAuth 2.1 (register → sign in → consent)──▶ auth.ticketlayer.com (identity)
│ issues access token
│ Streamable HTTP + Bearer token (org chosen at sign-in)
▼
Ticketlayer MCP server ──forwards your token──▶ Ticketlayer API
· validates issuer, audience, tool's scopes · your RBAC permissions
· org taken from the token · scopes cap every call
```
1. Your MCP client discovers the server's metadata at
`/.well-known/oauth-protected-resource/mcp` and finds the authorization server,
Ticketlayer's identity issuer at `https://auth.ticketlayer.com`.
2. It **registers itself** automatically (Dynamic Client Registration - no
pre-configuration, no client secret) and starts an OAuth sign-in, naming the
MCP server as the `resource` it wants a token for.
3. You sign in, **choose which organisation** the agent may act for, and **consent**
to the scopes it requested, each one named on the consent page.
4. The agent receives an access token and calls tools. The MCP server checks the
token is **for it** (its own address is in the token's audience), validates the
rest, and forwards it to the Ticketlayer API under your organisation and your
permissions.
The organisation is baked into the signed token, never chosen by the agent - so an
agent connected to one org can't reach another.
## Connect your client
The server URL is `https://mcp.ticketlayer.com/mcp`. Every client below discovers
auth and runs the login/consent flow in your browser the first time - there is no
API key to paste and nothing to pre-register.
### Claude Code (CLI)
Add the server, then authenticate from inside a session:
```bash
# Add it (available in the current project)
claude mcp add --transport http ticketlayer https://mcp.ticketlayer.com/mcp
# …or make it available across all your projects
claude mcp add --scope user --transport http ticketlayer https://mcp.ticketlayer.com/mcp
```
```text
# Start Claude Code, then run the /mcp command:
claude
> /mcp
# Select "ticketlayer" → Authenticate. Your browser opens to log in + consent;
# the token is stored and refreshed automatically.
```
Manage it:
```bash
claude mcp list # shows connection / auth status
claude mcp get ticketlayer # details for one server
claude mcp remove ticketlayer # remove it
```
To share it with a repo, commit a project-scoped `.mcp.json` at the repo root
(use `--scope project` when adding, or write the file directly):
```json
{
"mcpServers": {
"ticketlayer": {
"type": "http",
"url": "https://mcp.ticketlayer.com/mcp"
}
}
}
```
### Claude Desktop
Settings → **Connectors** → **Add custom connector** → name it `ticketlayer`
and paste `https://mcp.ticketlayer.com/mcp`. Claude Desktop registers itself and
opens the browser for login + consent. (Custom connectors require a paid plan.)
If you prefer a config file, or your Claude Desktop only supports stdio
servers, bridge through `mcp-remote` in `claude_desktop_config.json`
(Settings → Developer → Edit Config):
```json
{
"mcpServers": {
"ticketlayer": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ticketlayer.com/mcp"]
}
}
}
```
### Cursor
Add to `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project):
```json
{
"mcpServers": {
"ticketlayer": {
"url": "https://mcp.ticketlayer.com/mcp"
}
}
}
```
Then open **Settings → MCP** and authenticate the server.
### VS Code (GitHub Copilot)
```bash
code --add-mcp '{"name":"ticketlayer","type":"http","url":"https://mcp.ticketlayer.com/mcp"}'
```
…or commit `.vscode/mcp.json` (note VS Code uses the `servers` key):
```json
{
"servers": {
"ticketlayer": {
"type": "http",
"url": "https://mcp.ticketlayer.com/mcp"
}
}
}
```
### MCP Inspector (to test)
```bash
npx @modelcontextprotocol/inspector
```
Open it, choose transport **Streamable HTTP**, enter
`https://mcp.ticketlayer.com/mcp`, and **Connect** - it walks you through the OAuth
flow and lets you call tools by hand.
### Any other (stdio-only) client
For a client that only speaks stdio, bridge to the remote server with
[`mcp-remote`](https://www.npmjs.com/package/mcp-remote) (it handles the OAuth
flow for you):
```json
{
"mcpServers": {
"ticketlayer": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.ticketlayer.com/mcp"]
}
}
}
```
### Codex CLI
Add to `~/.codex/config.toml`:
```toml
[mcp_servers.ticketlayer]
url = "https://mcp.ticketlayer.com/mcp"
```
Once connected (any client), ask things like *"list my upcoming events"* or
*"how many orders did the jazz night take?"*.
## Scopes & permissions
Access is bounded **twice**: by the OAuth scopes you consent to, and by your own
role (RBAC). An agent gets the **intersection** of the two.
Each tool needs the scope for the kind of record it reads or writes. There is no
single scope for everything: you consent to each one by name.
| Scope | Tools |
|---|---|
| `events.read` | Events, occurrences, price schemes and offers |
| `venues.read` | Venues and layouts |
| `inventory.read` | Inventory and reservations |
| `orders.read` | Orders and order passes |
| `customers.read` | Customers and the tags each one holds |
| `tags.read` | Tag definitions |
| `saleschannels.read` | Sales channels and what each one lists |
| `accounts.read` | Accounts |
| `reports.read` | Analytics: reports, queries, saved reports and dashboards |
| `codes.read` | Discount and access codes (only when the agent asks for it) |
| `ledger.read` | Balances, statements, settlements and payouts (only when the agent asks for it) |
| `events.write` | **Create and update events** |
Your own profile, organisations, roles and permissions need no scope.
- **By default** an agent asks for the read scopes above except `codes.read` and
`ledger.read`: no codes, no money, and no writes. That is what the server's
sign-in challenge names, and what the authorization server registers an agent
for when it names no scope.
- **Anything more is asked for by name.** When the agent calls a tool its token
does not cover, the MCP server answers `403` with
`WWW-Authenticate: Bearer error="insufficient_scope", scope="..."`, naming the
scopes the token already has plus the ones the tool needs. A client that
supports step-up authorization signs you in again for them; otherwise
reconnect and grant them. The token is not sent to the API for that call.
- **Writes** need `events.write` as well as your permission to create or edit
events, and your MCP client asks you to confirm each one.
- Admin operations and sensitive actions are **never** reachable through an agent
token, regardless of your role.
## Tools
**Read (run automatically):** events, occurrences, price schemes, venues and
layouts, inventory and reservations, orders and order passes, customers and
their tags, tag definitions, sales channels and listings, accounts, your own
profile/permissions, **analytics** (see below), and, with their own scopes,
codes and the ledger.
**Write (require approval + a write scope):**
| Tool | Action |
|---|---|
| `createEvent` | Create a new event |
| `updateEvent` | Update an event's details or status |
Write tools are marked **destructive**, so your MCP client asks you to confirm each
one before it runs - that confirmation is the human-in-the-loop gate. Pricing and
seating-layout edits are not exposed as agent tools.
## Analytics & reporting
The agent can answer data questions ("how did the jazz night sell?", "revenue by
channel last month") through **Insights** - the governed Reporting layer that
serves the same numbers your dashboards show. These are read tools that need the
`reports.read` scope, which an agent asks for by default; no writes, no approval
needed.
The pattern is always two steps:
1. **`getReportsMeta`** - discover the available data models, **measures** and
**dimensions**. Always call this first; you can only query members it lists.
2. **`runReportsQuery`** - run an ad-hoc query built from those members.
`runReportsQuery` takes a query object:
```json
{
"query": {
"measures": ["sales_line_items.grossRevenue", "sales_line_items.ticketsSold"],
"dimensions": ["sales_line_items.eventName"],
"order": { "sales_line_items.grossRevenue": "desc" },
"limit": 10
}
}
```
…with optional `timeDimensions` (e.g.
`[{ "dimension": "orders.createdAt", "dateRange": "last 30 days", "granularity": "day" }]`)
and `filters`. Other read tools cover saved reports and dashboards
(`listSavedReports` / `runSavedReport`, `listDashboards` / `getDashboard`,
`listReportTemplates`) and pre-baked rollups (`getReportsSummary`,
`getReportsTimeseries`, `getReportsInventory`).
Two conventions to read results correctly:
- **Money is in integer minor units** (`"grossRevenue": "229350"` = £2,293.50) and
carries a **currency** dimension - never sum across currencies.
- **Timestamps are ISO 8601 UTC**; render in the venue/org timezone for display.
Everything is **tenant-scoped automatically** - a query only ever sees your own
organisation's data, enforced server-side in the Reporting layer.
## Security model
- **You can't exceed yourself.** Tools call the same permissioned routes the UI
uses, with your token. RBAC, the `404`-not-`403` rule, and the platform's
field-stripped views all apply unchanged.
- **Tokens are for this server.** The MCP server accepts only a token issued for
it: its address, `https://mcp.ticketlayer.com/mcp`, must be in the token's audience
(RFC 8707). A token issued to some other app, even one of yours, is refused.
- **One org per connection.** The org is carried in the signed token; switching orgs
means logging in again.
- **Least privilege.** Agent tokens are capped to their granted scopes, so even an
organisation owner's agent only does what it was consented to do.
- **No raw data path.** The MCP server holds no database connection and no model
key - it only forwards your authenticated calls.
## Limitations (today)
- Read-first: only `createEvent` / `updateEvent` writes are exposed.
- One organisation per token.
- Self-registered (DCR) agents are public PKCE clients with `https` or loopback
redirect URIs only.
- The client must send the `resource` parameter when it asks for a token, as the
MCP authorization spec requires; a token without it is refused.
---
# Sell tickets on your own website, end to end
Source: https://docs.ticketlayer.com/build/blueprints/sell-tickets-on-your-own-website
The whole path, in order. Each step links to the page with the detail.
## 1. Have something to sell
In Backstage create an **event** with at least one **occurrence** (a date and
time) and a **price scheme** (the ticket types and prices). Set it on sale.
With the API this is `POST /v1/events`, `POST /v1/events/{eventId}/occurrences`
and `POST /v1/events/{eventId}/price-schemes`; an [MCP agent](https://docs.ticketlayer.com/build/mcp)
can do the first two from a sentence.
## 2. Create a sales channel and a key
A **sales channel** is one place you sell: your website, a partner site, the
box office. Create one of type website in Backstage (Sales channels, New),
**list** the event on it, and under Keys create a **Publishable (browser)**
key. That `tlpk_` value is all the page needs.
Signing up creates the organisation with a website channel, a publishable
key and a sample event already listed, so a new organisation can skip this
step; see [Get a key](https://docs.ticketlayer.com/build/sell-on-your-own-site#3-get-a-key).
## 3. Put the elements on the page
Plain HTML or a site builder: the [HTML quickstart](https://docs.ticketlayer.com/build/quickstarts/html).
React: the [Vite quickstart](https://docs.ticketlayer.com/build/quickstarts/vite-react) or the
[Next.js quickstart](https://docs.ticketlayer.com/build/quickstarts/nextjs). The minimum is the loader
script and two tags:
```html
```
Match your brand with the `--tl-*` [theme tokens](https://docs.ticketlayer.com/reference/elements/#theming),
or set a theme on the channel in Backstage and the SDK applies it.
## 4. Checkout
Checkout is hosted. When the customer presses Checkout in the cart, the SDK
opens the hosted box office's `/embed/checkout` in an iframe modal, hands it
the session so it sees the same cart, and the iframe collects contact
details and payment on its own origin. Your page never sees card data. The
iframe talks back over `postMessage`; the SDK turns that into
`checkout:completed`, `checkout:error` and a close.
## 5. Confirm on your page
Listen for the completion and show the order:
```js
window.ticketlayer.ready.then((tl) =>
tl.on('checkout:completed', (order) => {
location.href = '/thanks?orderId=' + encodeURIComponent(order.id);
}),
);
```
On `/thanks`, `` reads `?orderId=` and renders the
reference, the lines, the totals and the tickets with QR codes and PDF links.
The customer also gets a confirmation email with the PDF passes.
## 6. Confirm on your server
Register a [webhook endpoint](https://docs.ticketlayer.com/build/webhooks) for
`order.confirmed` and `ticket.issued`. Verify `Ticketlayer-Signature`, then
update your CRM, mailing list or spreadsheet from the envelope. For
server-side reads (all orders for an event, a customer's history) use an
organisation API key with the [Backstage API](https://docs.ticketlayer.com/api/backstage/).
## 7. Returning customers
`` signs a customer in with a magic link (no passwords), and
`` lists their orders with the tickets. ``
redeems presale and promo codes against the cart. All three are on the
[Elements reference](https://docs.ticketlayer.com/reference/elements/). The customer token the magic
link produces stays in the Live session and escalates it to that buyer; the
publishable key is still what authenticates every request, and
[A buyer on your website](https://docs.ticketlayer.com/build/concepts/how-sales-work/website) draws the whole
sequence.
---
# Response format and errors
Source: https://docs.ticketlayer.com/build/responses-and-errors
Every Backstage API response, successful or not, uses the same envelope. This
page is the envelope itself; to make a call for the first time, follow
[Your first API call](https://docs.ticketlayer.com/build/quickstarts/first-api-call).
## Response Format
All API responses follow the JSend specification:
```json
{
"status": "success",
"data": {
"user": {
"id": "usr_123abc",
"email": "your@email.com",
"firstName": "John",
"lastName": "Doe",
"createdAt": "2026-01-01T00:00:00Z"
}
}
}
```
## Error Handling
When things go wrong, the API returns an error response:
```json
{
"status": "error",
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"moreInfo": "https://docs.ticketlayer.com/error/UNAUTHORIZED?request=req_01j9x2k3m4n5p6q7r8s9t0v1w2"
},
"metadata": {
"requestId": "req_01j9x2k3m4n5p6q7r8s9t0v1w2"
}
}
```
| Field | What it is |
|---|---|
| `error.code` | A stable identifier for what went wrong. Branch on this, never on `message`. |
| `error.message` | A sentence for a person reading a log. Its wording can change. |
| `error.moreInfo` | The address of this site's page for the code, `/error/{CODE}`, with the response's request id in the `request` query parameter. Follow it for what the code means and what to do. |
| `metadata.requestId` | The id of this request. Quote it when you contact support. |
Every code the API sends is listed, grouped by HTTP status, at
[Error codes](https://docs.ticketlayer.com/error).
The TypeScript SDK throws typed errors you can catch:
```typescript
try {
const user = await client.users.getMe();
} catch (error) {
if (error.status === 401) {
console.log('Not authenticated');
} else if (error.status === 403) {
console.log('Permission denied');
} else {
console.log('API error:', error.message);
}
}
```
---
# SDKs
Source: https://docs.ticketlayer.com/reference/sdk
Ticketlayer ships two official TypeScript SDKs, one per API surface. Both are
generated from the same OpenAPI documents that power the API reference, so
their types always match the contract, and each sends the dated API version
(`TL-Version`) it was generated against.
## Which packages are on npm today
| Package | What | npm |
|---|---|---|
| `@ticketlayer/backstage` | Backstage API client (servers, staff tools, embed loader) | **Published**, 0.1.x |
| `@ticketlayer/live` | Live API client for browsers, React Native and servers (`/server`) | Not yet: publishes when its release workflow runs |
| `@ticketlayer/elements` | The `tl-*` web components | Not yet: same |
| `@ticketlayer/elements-react` | React wrappers for the elements | Not yet: same |
`@ticketlayer/backstage-sdk` was the old name of the Backstage package and is
no longer maintained; use `@ticketlayer/backstage`. Until the three Live
packages publish, the browser path that works is the CDN build
(`cdn.ticketlayer.com/v1/ticketlayer.js`); see the
[HTML quickstart](https://docs.ticketlayer.com/build/quickstarts/html).
## [Backstage SDK](https://docs.ticketlayer.com/reference/sdk/backstage) - `@ticketlayer/backstage`
The server-side SDK for the **Backstage API**: events, venues, inventory,
sales channels, orders, customers, payments, webhooks, organisations and the
full admin surface. Use it in Node.js backends and internal tooling with an
organisation API key, or in staff apps with an access token from the identity issuer. It also
carries `@ticketlayer/backstage/embed`, the loader for Backstage widgets in
your own app.
```bash
npm install @ticketlayer/backstage
```
See the [getting started guide](https://docs.ticketlayer.com/build/sdks/typescript) and the
[full reference](https://docs.ticketlayer.com/reference/sdk/backstage).
## [Live SDK](https://docs.ticketlayer.com/reference/sdk/live) - `@ticketlayer/live`
The buyer-facing SDK for the **Live API**: sessions, listings, the cart, the
hosted checkout modal, customer sign-in and orders. It runs in the browser
and React Native, publishes itself on `window.Ticketlayer` for the
[Elements](https://docs.ticketlayer.com/reference/elements/), and has a stateless server entry
(`@ticketlayer/live/server`) for Next.js and edge runtimes.
```bash
npm install @ticketlayer/live
```
See the [Next.js quickstart](https://docs.ticketlayer.com/build/quickstarts/nextjs) and the
[full reference](https://docs.ticketlayer.com/reference/sdk/live).
## Reference is auto-generated
These reference pages are generated from each package's TypeScript source
with TypeDoc (`npm run docs:sdk:gen` in the docs repository) and refreshed
whenever the SDKs are rebuilt.
---
# API versions
Source: https://docs.ticketlayer.com/api/backstage/versions
The Backstage API uses **dated versions** - each version is named after the day it was
released, for example `2026-10-02`. The current version is **`2026-10-02`**.
Within a version the API only ever changes additively: new endpoints, new optional
parameters and new response fields may appear, but existing behaviour never changes.
Breaking changes are only ever introduced in a new dated version, so an integration
pinned to a version keeps working unchanged.
## How versioning works
- **Pin with the `TL-Version` header.** Send the version date on every request to pin
the API behaviour your integration expects. Responses echo the version that served
the request in a `TL-Version` response header.
- **Organisations are pinned at signup.** When an organisation is created it is pinned
to the then-current version. Requests that omit `TL-Version` use the organisation's
pinned version, so existing integrations are never broken by a new release.
- **SDK majors pin a version.** Each major release of an official SDK hardcodes the
version it speaks (the current TypeScript SDK sends `2026-10-02`). Upgrading
the SDK is how most integrations move between API versions.
- **Additive changes only within a version.** We may add endpoints, optional request
fields and response fields at any time without releasing a new version. Your
integration should ignore response fields it does not recognise.
- **Twelve-month deprecation windows.** When a version is deprecated you have at least
12 months before it is sunset. While deprecated, responses include
[`Deprecation`](https://datatracker.ietf.org/doc/html/rfc9745) and
[`Sunset`](https://datatracker.ietf.org/doc/html/rfc8594) headers telling you when
the version stops being served.
### Pinning a version
```bash
curl https://api.ticketlayer.com/v1/events \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "TL-Version: 2026-10-02"
```
### Discovering versions at runtime
The full version list, including deprecation and sunset dates, is available from the
API itself:
```bash
curl https://api.ticketlayer.com/v1/meta/versions
```
## Version status
| Version | Status | Summary | Deprecated | Sunset |
|---------|--------|---------|------------|--------|
| **`2026-10-02`** | Current | The checkout loop (TKT-575): checkout answers a `checkout` object saying what happens next, and reads TL-Client-Capabilities. Also the payments programme (P-TKT-46), additive: payment shares held by more than one party, the payment method catalogue and per-channel method policies, payments staff record and split tender, orders awaiting payment with due dates and send-before-payment, cash sessions, processing costs chosen by the connection owner, fee components, surcharges and all-in headline prices, the cart as a quote, tax registrations, tax lines and tax documents, Adyen and PayPal in merchant-credentials mode, pass identifiers, holders, transfers and signed barcodes, delivery methods and fulfilment, security policies and wallet passes, holder slots, add-ons and packages, stored value, account credit and gift cards. New operations, optional request fields, new response fields, new enum values that appear only once an organisation uses the feature that makes them (an order awaiting payment, a gift card line), and new webhook event types (payment.recorded, cash_session.closed). Nothing a 2026-06-13 caller sends or reads changes meaning, so there are no transforms and 2026-06-13 callers are served the additions too. | - | - |
| `2026-06-13` | Supported | Baseline dated version - all prior behaviour, no transforms. | - | - |
## Version history
### 2026-10-02
Current
The checkout loop (TKT-575): checkout answers a `checkout` object saying what happens next, and reads TL-Client-Capabilities. Also the payments programme (P-TKT-46), additive: payment shares held by more than one party, the payment method catalogue and per-channel method policies, payments staff record and split tender, orders awaiting payment with due dates and send-before-payment, cash sessions, processing costs chosen by the connection owner, fee components, surcharges and all-in headline prices, the cart as a quote, tax registrations, tax lines and tax documents, Adyen and PayPal in merchant-credentials mode, pass identifiers, holders, transfers and signed barcodes, delivery methods and fulfilment, security policies and wallet passes, holder slots, add-ons and packages, stored value, account credit and gift cards. New operations, optional request fields, new response fields, new enum values that appear only once an organisation uses the feature that makes them (an order awaiting payment, a gift card line), and new webhook event types (payment.recorded, cash_session.closed). Nothing a 2026-06-13 caller sends or reads changes meaning, so there are no transforms and 2026-06-13 callers are served the additions too.
#### Breaking changes
- POST /carts/{cartId}/checkout (Live) and POST /cart/{cartId}/checkout (staff) answer a `checkout` object: `status` (requires_input, requires_approval, requires_payment, processing, complete), `nextAction`, `capabilitiesApplied`, `deferred` and `expiresAt`. Earlier versions do not receive it.
- `order` in the checkout answer is null while `checkout.status` is requires_input: a requirement must be answered before the order is made. On earlier versions every requirement takes its fallback, so the order is always made or the checkout refused.
- TL-Client-Capabilities is read at checkout: the API asks the client inline for what it declared, sends a `redirect` to the hosted completion page for what it did not when it declared `redirect`, and applies the requirement's fallback otherwise. Earlier versions ignore the header.
- Checking out a cart that is already checked out answers the checkout as it stands (requires_approval, requires_payment, processing or complete) instead of 409 CART_NOT_ACTIVE, so the call is safe to repeat; an expired or cancelled order answers 409 CHECKOUT_CLOSED.
#### Migrating
Review each change above and update your integration to handle the new behaviour, then update the `TL-Version` header (or upgrade to an SDK release that targets `2026-10-02` or later). Until you do, your requests continue to behave exactly as they did on your pinned version.
---
### 2026-06-13
Supported
Baseline dated version - all prior behaviour, no transforms.
No breaking changes - this is the baseline dated version.
## See also
- [API changelog](./changelog) - additive changes and fixes within versions
- [Backstage API introduction](https://docs.ticketlayer.com/api/backstage/)
---
# Elements catalogue {#elements-catalogue}
Rendered from elements.catalogue.json (version 5.6.0, generated 2026-10-03), the artefact the Elements package generates from its own components. Machine-readable copy: https://docs.ticketlayer.com/elements.catalogue.json
## tl-best-available {#element-tl-best-available}
The best seats for a party: a few options that differ (the best, a cheaper one, one closer or further back), each with the view from it, the row and seats, why it was chosen and the price. Hovering an option rings its seats on the seat map; taking it holds them. With the quantity picker and the preferences above it.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-best-available
- SDK calls: cart.get, cart.on, cart.reserveSeat, cart.reserveSeats, client.emit, client.on, events.getOffers, seating.bestAvailable, seating.getSeatmap, seating.getStaffAccess, seating.getView, seating.watchStatus
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| event-id | eventId | string \| undefined | no | The event, for its prices |
| occurrence-id | occurrenceId | string | yes | The occurrence to find seats in |
| options | options | number | no | How many options to show |
| quantity | quantity | number | no | How many tickets to start with |
| show-preferences | showPreferences | boolean | no | Show the preferences under "How many tickets?" |
| show-quantity | showQuantity | boolean | no | Show "How many tickets?" above the options |
| staff | staff | boolean | no | Box office mode: one option at a time with "Next best", a keypad for how many, the channel's own number of options, and a single seat gap sold after a confirm where the client allows it |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-best-available__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Suggestions could not be found, or an option could not be held |
| tlOptionSelect | { seats: number[]; facet: string; } | An option was taken and its seats are held |
## tl-booking-holders {#element-tl-booking-holders}
Manage my booking, people first: each holder with their ticket and add-ons, to name or rename, move an add-on to someone else, place add-ons nobody has yet, and activate a posted wristband.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-booking-holders
- SDK calls: orders.activateWristband, orders.assignAddOn, orders.holderLink, orders.holders, orders.nameHolder
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-warning
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| allow-activation | allowActivation | boolean | no | Offer to activate a wristband that came in the post, on a holder whose posted band waits for them. |
| guest-page-url | guestPageUrl | string | no | The page a guest's link opens, the one carrying `tl-holder-pass`; the link adds `order`, `slot` and `holder_token` to it. Without it, this page without its query. |
| heading | heading | string | no | The element's heading. |
| order-id | orderId | string | yes | The booking to manage. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-booking-holders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when a change is refused or the booking cannot be loaded, with the message shown. |
| tlGuestLinkCreated | TlGuestLinkCreated | Emitted when the booker makes a guest's own link, with the link and when it expires. |
| tlHoldersChanged | TlHoldersChanged | Emitted after a holder is named or an add-on is moved or placed. |
## tl-booking-pass {#element-tl-booking-pass}
The booking pass: one barcode for the whole order, listing everyone it covers until each has their own pass or wristband.
- category: tickets; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-booking-pass
- SDK calls: orders.get, orders.holders
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-font-family, --tl-font-family-mono, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-radius-md, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The element's heading. |
| order-id | orderId | string | yes | The booking whose pass to show. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-booking-pass__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the booking pass cannot be loaded. |
## tl-buy-modal {#element-tl-buy-modal}
The buy-tickets modal: a date, then tickets, then payment, under a header carrying the channel's and the event's identity, beside a basket showing what the buyer is paying for. The hosted checkout opens inside it where the page allows.
- category: purchase; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-buy-modal
- SDK calls: cart.get, cart.on, events.get, seating.getPointer, theme.get
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-slow, --tl-easing-emphasised, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-2xl, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-overlay, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| channel-header | channelHeader | "blurred" \| "hidden" \| "plain" \| undefined | no | Whether and how the channel's logo and name are drawn above the event: hidden, over the blurred image, or on the primary colour. Defaults to the channel theme's design.modalHeader.channel, then hidden |
| event-id | eventId | string | yes | Event to sell tickets for |
| header | header | "blurred" \| "plain" \| undefined | no | How the event band is drawn: the event image blurred behind a square of it, or plain on the background. Defaults to the channel theme's design.modalHeader.event, then blurred |
| occurrence-id | occurrenceId | string \| undefined | no | Optionally open straight to ticket selection for this occurrence |
| payment-return | paymentReturn | string \| undefined | no | A payment method coming back to the page: open straight on payment to finish it. Set by |
| size | size | "fixed" \| "wide" | no | A fixed 1040 by 640 frame that keeps its size through every step, or nearly the whole screen. Full screen on a phone either way |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-buy-modal__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlCheckout | void | The tickets are in the cart and the host's own checkout takes over (the page cannot show the checkout inside the modal) |
| tlClose | void | The buyer dismissed the modal |
| tlComplete | { order: unknown; } | The buyer paid, inside the modal; the modal shows its confirmation |
| tlError | { message: string; } | The event or its tickets couldn't be loaded, the tickets couldn't be added, or the checkout couldn't be opened |
## tl-buy-tickets-button {#element-tl-buy-tickets-button}
A buy-tickets button in one of four sale states, which opens the purchase modal.
- category: purchase; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-buy-tickets-button
- SDK calls: event.openModal
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-error-foreground, --tl-font-family, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-foreground, --tl-info, --tl-info-foreground, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-5, --tl-spacing-6, --tl-warning, --tl-warning-foreground
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| currency | currency | string | no | Currency code for price formatting |
| disabled | disabled | boolean | no | Disabled state |
| event-id | eventId | string | yes | Event ID to purchase tickets for |
| full-width | fullWidth | boolean | no | Full width button |
| label | label | string \| undefined | no | Button label override (default depends on state) |
| occurrence-id | occurrenceId | string \| undefined | no | Optional pre-selected occurrence ID |
| price | price | number \| undefined | no | Price to display (in minor units e.g. pence) |
| price-prefix | pricePrefix | string | no | Show price prefix text |
| size | size | "lg" \| "md" \| "sm" | no | Button size |
| state | state | "available" \| "comingSoon" \| "soldOut" \| "waitlist" | no | Button state - determines colour, label, and icon |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-buy-tickets-button__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| variant | variant | "outline" \| "primary" \| "secondary" | no | Button variant |
| Event | Detail | When |
|---|---|---|
| tlClick | { eventId: string; occurrenceId?: string \| undefined; } | Emitted when button is clicked (before modal opens) |
## tl-buy-tickets-wrapper {#element-tl-buy-tickets-wrapper}
Wrap any content and open the purchase modal when it is activated.
- category: purchase; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-buy-tickets-wrapper
- SDK calls: event.openModal
- theme tokens: --tl-radius-sm, --tl-ring
- slots: (default)
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| disabled | disabled | boolean | no | Whether the wrapper is disabled |
| event-id | eventId | string | yes | The event ID to purchase tickets for |
| occurrence-id | occurrenceId | string \| undefined | no | Optional specific occurrence ID (skips occurrence selector) |
| Event | Detail | When |
|---|---|---|
| tlClose | void | Emitted when the modal closes |
| tlComplete | OrderResult | Emitted when purchase completes successfully |
| tlError | { message: string; } | Emitted when an error occurs |
## tl-cart {#element-tl-cart}
The buyer's cart: its lines, the money rows the surface reported and a checkout button.
- category: cart; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-cart
- SDK calls: cart.get, cart.on, cart.releaseSeat, cart.removeItem
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-8, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| checkout-label | checkoutLabel | string | no | Checkout button label |
| compact | compact | boolean | no | Compact mode - hides some details |
| show-checkout-button | showCheckoutButton | boolean | no | Show checkout button |
| show-remove-buttons | showRemoveButtons | boolean | no | Show remove item buttons |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlCartUpdated | { cart: TlCartData; } | Emitted when cart is updated |
| tlCheckout | { cart: TlCartData; } | Emitted when checkout is clicked |
| tlError | { message: string; } | Emitted on error |
| tlItemRemoved | { itemId: string; } | Emitted when item is removed |
| tlSeatRemoved | { occurrenceId: string; seat: number; } | Emitted when a held seat is given back |
## tl-cart-badge {#element-tl-cart-badge}
A cart icon carrying the number of items in the cart, kept current from the cart bus.
- category: cart; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-cart-badge
- SDK calls: cart.get, cart.on
- theme tokens: --tl-background, --tl-duration-fast, --tl-duration-normal, --tl-easing-emphasised, --tl-easing-standard, --tl-font-family, --tl-font-size-2xs, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-md, --tl-ring
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| badge-only | badgeOnly | boolean | no | Badge only mode - only shows the count bubble, no icon or button |
| icon | icon | string \| undefined | no | Custom icon (emoji or text). If not provided, uses cart SVG |
| show-empty | showEmpty | boolean | no | Show the badge even when count is 0 |
| size | size | "lg" \| "md" \| "sm" | no | Size variant |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart-badge__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlClick | { itemCount: number; } | Emitted when the badge is clicked |
## tl-cart-drawer {#element-tl-cart-drawer}
A slide-in panel holding the cart, opened by the cart intent on the client bus.
- category: cart; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-cart-drawer
- SDK calls: client.on
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-slow, --tl-easing-emphasised, --tl-easing-standard, --tl-font-family, --tl-font-family-heading, --tl-font-size-2xs, --tl-font-size-md, --tl-font-size-xl, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-overlay, --tl-radius-full, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | Heading shown at the top of the panel. |
| open | open | boolean | no | Controlled open state (also driven by the `ui:cart-requested` intent). |
| side | side | "left" \| "right" | no | Edge the panel slides in from. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart-drawer__*` class. Without it the element is styled, and each node is a `::part()` of the same name. The cart inside the panel follows the same mode. |
| Event | Detail | When |
|---|---|---|
| tlClose | void | - |
| tlOpen | void | - |
## tl-checkout-credit {#element-tl-checkout-credit}
The checkout's gift cards and credit: apply the signed-in buyer's account credit, or a gift card by its code, see what each covers, and remove either before paying. The rest is paid by the payment method chosen below.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-checkout-credit
- SDK calls: cart.options, cart.quote, storedValue.applyCredit, storedValue.applyGiftCard, storedValue.credit, storedValue.removeTender, storedValue.tenders
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-family-mono, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The section's heading. |
| show-credit | showCredit | boolean | no | Offer the signed-in buyer's account credit, when they have some in this order's currency. |
| show-gift-cards | showGiftCards | boolean | no | Offer paying with a gift card by its code. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-credit__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when a gift card or credit cannot be applied, with the message shown. |
| tlTendersChanged | TlTendersChanged | Emitted when gift cards or credit are applied or removed: what they pay and what is left. Never carries a code. |
## tl-checkout-holders {#element-tl-checkout-holders}
The checkout's "Who is this for?" section: a name for each guest, vehicle or group the cart makes, and who each add-on goes to, "one each" when that is automatic.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-checkout-holders
- SDK calls: cart.get, cart.holders, cart.setHolders
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The section's heading. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-holders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when a change is refused or the holders cannot be loaded, with the message shown. |
| tlHoldersSaved | TlHoldersSaved | Emitted after names or add-on choices are saved, with how many are named and how many add-ons are still to place. |
## tl-checkout-summary {#element-tl-checkout-summary}
The checkout's order summary, priced from the cart's quote: the tickets with the fees included in them, each separate fee, any discount and the total checkout charges. It re-prices as delivery and payment are chosen.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-checkout-summary
- SDK calls: cart.quote, storedValue.tenders
- theme tokens: --tl-border, --tl-error, --tl-font-family, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The summary's heading. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-summary__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the quote cannot be read. |
| tlQuoteUpdated | TlQuoteUpdated | Emitted when the quote moves to a new total. |
## tl-checkout-wrapper {#element-tl-checkout-wrapper}
Wrap any content and open the checkout modal when it is activated.
- category: checkout; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-checkout-wrapper
- SDK calls: checkout.openModal
- theme tokens: --tl-radius-sm, --tl-ring
- slots: (default)
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| disabled | disabled | boolean | no | Whether the wrapper is disabled |
| Event | Detail | When |
|---|---|---|
| tlClose | void | Emitted when the modal closes |
| tlComplete | OrderResult | Emitted when checkout completes successfully |
| tlError | { message: string; } | Emitted when an error occurs |
## tl-concierge {#element-tl-concierge}
Concierge: search the help articles, ask the AI when the channel has it on, see my orders after a code typed into the widget, or contact the organiser. Hidden unless the channel turns it on.
- category: support; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-concierge
- SDK calls: auth.authenticated, auth.customer, concierge.send, on, orders.mine
- theme tokens: --tl-background, --tl-border, --tl-border-hover, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-family-mono, --tl-font-size-2xl, --tl-font-size-2xs, --tl-font-size-3xl, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-info-bg, --tl-input, --tl-muted, --tl-muted-foreground, --tl-overlay, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-secondary, --tl-secondary-foreground, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6, --tl-success, --tl-success-foreground, --tl-warning-bg
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| cart-id | cartId | string \| undefined | no | Context: the cart, when the host has one. |
| embedded | embedded | "buy-modal" \| "checkout" \| "confirmation" \| "my-orders" \| undefined | no | Set by tl-concierge-slot: the element it is inside, whose placement the channel switches on. |
| event-id | eventId | string \| undefined | no | Context: the event the customer is looking at. |
| occurrence-id | occurrenceId | string \| undefined | no | Context: the occurrence the customer is looking at. |
| open | open | boolean | no | Launcher only: start with the panel open. |
| order-id | orderId | string \| undefined | no | Context: the order the customer is looking at. |
| orders-view | ordersView | "element" \| "list" | no | After verification: hand off to tl-my-orders (element), or list the orders in the widget (list). |
| placement | placement | "inline" \| "launcher" \| "page" | no | Where it sits: a launcher bubble, an inline panel, or a full page. |
| query | query | string \| undefined | no | A search to start with, shown in the field: what the customer typed into the embedded field. |
| section | section | "ask" \| "contact" \| "orders" \| "search" \| undefined | no | The tab to open on, when the channel offers it. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-concierge__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlConciergeCardAction | { proposal: string; proposalId: string; action: "cancel" \| "confirm"; outcome: "failed" \| "cancelled" \| "executed"; code: string \| null; } | The customer answered a confirm card the engine sent: confirmed (executed or failed, with the code) or cancelled. |
| tlConciergeContactSent | { caseId: string \| null; reference: string \| null; } | The contact form opened a case. |
| tlConciergeHandover | { caseId: string \| null; } | The assistant handed the conversation to a person. |
| tlConciergeToggle | { open: boolean; } | The launcher panel opened or closed. |
| tlConciergeVerified | { authenticated: boolean; } | The code was accepted and the customer is signed in. |
| tlError | { message: string; } | Something the customer asked for failed. |
## tl-concierge-slot {#element-tl-concierge-slot}
The "Need help?" another element hosts: renders nothing unless the channel has Concierge on for that placement, then a help field that opens tl-concierge inline with the host's context and what was typed.
- category: support; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-concierge-slot
- SDK calls: concierge.send
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-font-family, --tl-font-family-heading, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| cart-id | cartId | string \| undefined | no | Context: the cart. |
| event-id | eventId | string \| undefined | no | Context: the event. |
| label | label | string | no | The text of the toggle. |
| occurrence-id | occurrenceId | string \| undefined | no | Context: the occurrence. |
| order-id | orderId | string \| undefined | no | Context: the order. |
| placement | placement | "buy-modal" \| "checkout" \| "confirmation" \| "my-orders" | yes | Which element hosts it; the channel switches each placement on separately. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-concierge-slot__*` class. |
| Event | Detail | When |
|---|---|---|
| tlConciergeToggle | { open: boolean; } | The help panel opened or closed. |
## tl-delivery-picker {#element-tl-delivery-picker}
The checkout's Delivery section: how the tickets reach the buyer, each method with what it adds, and the reason beside one that cannot be chosen. Choosing re-prices the cart's quote.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-delivery-picker
- SDK calls: cart.choose, cart.options
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-primary, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| auto-choose | autoChoose | boolean | no | Choose the only method that can be chosen when nothing is chosen yet, so the quote includes it. |
| collapse | collapse | boolean | no | Fold the section to one line when its only method is chosen, with Change when there is more to see. |
| heading | heading | string | no | The section's heading. |
| unavailable | unavailable | "all" \| "none" \| "relevant" | no | Which methods that cannot be chosen are shown, disabled with the reason: `relevant` (closed by a cut-off, business customers only), `all`, or `none`. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-delivery-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlDeliveryChosen | { method: string; total: number \| null; } | Emitted once a delivery method is chosen, with the re-priced total. |
| tlError | { message: string; } | Emitted when a choice is refused or the options cannot be read. |
## tl-event-card {#element-tl-event-card}
One event as a card: its image, name, next date, venue, what a ticket costs with fees, and how it is selling.
- category: discovery; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-event-card
- SDK calls: events.get
- theme tokens: --tl-background, --tl-border, --tl-duration-normal, --tl-duration-slow, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-info, --tl-info-bg, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-full, --tl-radius-lg, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-success, --tl-warning, --tl-warning-bg
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| - | event | TlEventCardEvent \| undefined | no | Event data to display (tl-event-list passes this) |
| event-id | eventId | string \| undefined | no | Load the event from the SDK by id when no `event` is given, so a single card works standalone (e.g. on a Webflow page). |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-event-card__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlClick | { eventId: string; } | Emitted when the card is clicked |
| tlError | { message: string; } | Emitted when the event could not be loaded by id |
## tl-event-list {#element-tl-event-list}
What the channel is selling, as a grid of event cards, optionally narrowed to one category.
- category: discovery; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-event-list
- SDK calls: events.list
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-radius-full, --tl-radius-lg, --tl-spacing-1, --tl-spacing-2, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6, --tl-spacing-8
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| category | category | string \| undefined | no | Category filter |
| columns | columns | number | no | Columns for grid layout (1, 2, 3, or 4) |
| limit | limit | number | no | Maximum number of events to display |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-event-list__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the listing could not be read |
| tlEventClick | { eventId: string; } | Emitted when an event card is clicked |
## tl-gift-card-balance {#element-tl-gift-card-balance}
Check what is on a gift card by its code: the amount, until when, and whether it can be used. Too many tries in a minute are met with a friendly wait.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-gift-card-balance
- SDK calls: storedValue.balance
- theme tokens: --tl-background, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-family-mono, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| button-label | buttonLabel | string | no | The check button label. |
| heading | heading | string | no | The element's heading. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-gift-card-balance__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlBalanceChecked | TlBalanceChecked | Emitted when a card is found: its balance and status, never its code. |
| tlError | { message: string; } | Emitted when a balance cannot be checked, with the message shown. |
## tl-gift-card-shop {#element-tl-gift-card-shop}
Buy a gift card: choose the card, an amount it comes in, email or post (post only with a posted order), the recipient and a message, and add it to the basket. The cards in the basket are listed with a Remove each.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-gift-card-shop
- SDK calls: cart.addGiftCard, cart.get, cart.quote, cart.removeGiftCard, storedValue.products
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| button-label | buttonLabel | string | no | The add button's label; "Add £50.00 gift card to basket" when left empty. |
| heading | heading | string | no | The shop's heading. |
| product-id | productId | string | no | Show only this gift card (its id); every gift card on sale when left empty. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-gift-card-shop__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the gift cards cannot be loaded or one cannot be added. |
| tlGiftCardAdded | TlGiftCardAdded | Emitted once a gift card is in the basket. |
## tl-holder-pass {#element-tl-holder-pass}
A named guest's own page, from the link the booker sent: their ticket and extras, their name under the event's rules, and their own pass to download. Nothing else of the booking: no refunds, transfers or moving extras.
- category: tickets; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-holder-pass
- SDK calls: orders.holder, orders.nameHolder, orders.ticketPdf, orders.useHolderLink
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The element's heading. |
| order-id | orderId | string | no | The booking; read from the link's `order` when not set. |
| slot-id | slotId | string | no | The guest's holder (`hsl_...`); read from the link's `slot` when not set. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-holder-pass__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the pass cannot be loaded or a change is refused, with the message shown. |
| tlHolderNamed | TlHolderNamed | Emitted once the API has saved the name the guest gave. |
| tlPassDownloadRequested | TlPassDownloadRequested | The guest asked for their pass as a file. Announced before the harness does anything, whether or not the download goes on to succeed. |
## tl-login {#element-tl-login}
Magic-link sign-in: an email in, a link or a six-digit code back, and a session. No password is ever handled.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-login
- SDK calls: auth.exchange, auth.logout, auth.requestMagicLink, on
- theme tokens: --tl-background, --tl-border, --tl-border-hover, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-family-heading, --tl-font-family-mono, --tl-font-size-2xl, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6, --tl-success, --tl-success-foreground
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| allow-code-entry | allowCodeEntry | boolean | no | Offer a "enter the code" path alongside the emailed link (default true). |
| heading | heading | string | no | Heading for the email step. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-login__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlAuthChanged | TlAuthChanged | - |
| tlError | { message: string; } | - |
| tlMagicLinkSent | { email: string; } | - |
## tl-map-key {#element-tl-map-key}
The seat map's key: a minimap of the whole venue with the part the map shows outlined (choose a section to go there), and the price bands, each category's colour and lowest price, with the seat states drawn in shape as well as colour. In staff mode a Status tab counts the seats in each state the box office's map draws: available by price category, this sale, each hold and each allocation by name, comps, sold, other carts and killed.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-map-key
- SDK calls: cart.get, cart.on, client.emit, client.on, events.getOffers, seating.getSeatmap, seating.getStaffState, seating.getTicketTypes, seating.watchStatus
- theme tokens: --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-font-family, --tl-font-size-2xs, --tl-font-size-xs, --tl-foreground, --tl-map-background, --tl-map-plate, --tl-map-section, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-seat-allocated, --tl-seat-comp, --tl-seat-held, --tl-seat-killed, --tl-seat-reserved, --tl-seat-selected, --tl-seat-selected-mark, --tl-seat-selected-ring, --tl-seat-unavailable, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-stage-fill
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| collapsed | collapsed | boolean | no | Start closed, showing only the tabs |
| event-id | eventId | string \| undefined | no | The event, for the prices; without it the prices the page has already read |
| occurrence-id | occurrenceId | string | yes | The occurrence whose seat map this is the key to |
| staff | staff | boolean | no | Box office mode, on a client that offers the staff capabilities: a Status tab counting available seats by price category, this sale, each hold and each allocation by name, comps, sold, other carts and killed seats |
| tab | tab | "bands" \| "status" \| "venue" | no | The tab to open on: the venue minimap, the price bands, or in staff mode the seat states |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-map-key__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlSectionRequest | { sectionId: string \| null; } | The buyer chose a section on the minimap (null: the whole venue); the map is asked to show it |
## tl-my-credit {#element-tl-my-credit}
Your credit: the signed-in buyer's account credit with this organisation, each balance with its expiry and whether it is on hold, and its activity (what was put on it and spent, when, and on which order) a page at a time.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-my-credit
- SDK calls: auth.authenticated, storedValue.credit, storedValue.creditActivity
- theme tokens: --tl-border, --tl-error, --tl-font-family, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-warning
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | The element's heading. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-my-credit__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the credit cannot be loaded. |
## tl-my-orders {#element-tl-my-orders}
The signed-in customer's orders: event, date, ticket count, total and status, each row expanding to its tickets.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-my-orders
- SDK calls: auth.authenticated, on, orders.mine
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-weight-heading, --tl-foreground, --tl-info, --tl-info-bg, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-full, --tl-radius-lg, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-8, --tl-success, --tl-warning, --tl-warning-bg
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| columns | columns | number | no | Columns for the expanded ticket grid. |
| empty-message | emptyMessage | string | no | Copy for the empty state. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-my-orders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | - |
| tlOrderSelect | { orderId: string; } | - |
## tl-occurrence-selector {#element-tl-occurrence-selector}
A smart occurrence selector component that fetches and displays event occurrences from the Ticketlayer API.
- category: discovery; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-occurrence-selector
- SDK calls: events.get
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6, --tl-spacing-8, --tl-warning
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| display-mode | displayMode | "calendar" \| "calendar-slots" \| "dates" \| "list" \| "slots" \| undefined | no | Force a specific display mode (auto-detected if not set) |
| event-id | eventId | string | yes | Event ID to fetch occurrences for |
| occurrence-id | occurrenceId | string \| undefined | no | The occurrence already picked: the element opens on its day and marks it |
| orientation | orientation | "auto" \| "horizontal" \| "vertical" | no | Layout orientation (auto = horizontal on desktop, vertical on mobile) |
| - | theme | TlOccurrenceSelectorTheme \| undefined | no | Theme configuration |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-occurrence-selector__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlSelect | { occurrence: TlOccurrence; } | Emitted when an occurrence is selected |
## tl-order-confirmation {#element-tl-order-confirmation}
The thank-you block: order reference, email, event and date, the ticket lines, the money rows and the tickets.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-order-confirmation
- SDK calls: auth.customer, events.get, events.list, on, orders.get
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-family-mono, --tl-font-size-2xl, --tl-font-size-2xs, --tl-font-size-3xl, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-radius-full, --tl-radius-lg, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6, --tl-success, --tl-success-foreground
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| columns | columns | number | no | Columns for the ticket grid. |
| customer-email | customerEmail | string \| undefined | no | Email to show when the order view carries none (a host that collected it at checkout). |
| order-id | orderId | string \| undefined | no | The order to show. Falls back to `?orderId=` in the page URL. |
| show-tickets | showTickets | boolean | no | Mount tl-order-tickets under the summary (default true). |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-order-confirmation__*` class. Without it the element is styled, and each node is a `::part()` of the same name. The ticket grid inside follows the same mode. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | - |
| tlOrderLoaded | { order: TlOrderData; } | - |
## tl-order-tickets {#element-tl-order-tickets}
Every ticket in an order as a scannable card, waiting out issuance rather than reporting an empty order as empty.
- category: tickets; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-order-tickets
- SDK calls: orders.get
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-foreground, --tl-muted-foreground, --tl-radius-lg, --tl-radius-md, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-spacing-6
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| columns | columns | number | no | Columns in the grid (default 2). |
| order-id | orderId | string | yes | The order whose tickets to show. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-order-tickets__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
## tl-payment-picker {#element-tl-payment-picker}
The checkout's Payment section: the ways this order can be paid, each with any surcharge it adds, and the reason beside one that cannot be chosen. Choosing re-prices the cart's quote.
- category: checkout; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-payment-picker
- SDK calls: cart.choose, cart.options
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-primary, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| auto-choose | autoChoose | boolean | no | Choose the only method that can be chosen when nothing is chosen yet, so the quote includes it. |
| collapse | collapse | boolean | no | Fold the section to one line when its only method is chosen, with Change when there is more to see. |
| heading | heading | string | no | The section's heading. |
| unavailable | unavailable | "all" \| "none" \| "relevant" | no | Which methods that cannot be chosen are shown, disabled with the reason: `relevant` (closed by a cut-off, business customers only), `all`, or `none`. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-payment-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when a choice is refused or the options cannot be read. |
| tlPaymentChosen | { method: string; total: number \| null; } | Emitted once a payment method is chosen, with the re-priced total. |
## tl-price-picker {#element-tl-price-picker}
The ticket type of a held seat: Adult, Child, Concession and the rest of its category's types, as a dropdown or a segmented control, for one seat or every seat held for the occurrence. A change keeps the seat.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-price-picker
- SDK calls: cart.get, cart.on, cart.setSeatType, events.getOffers, seating.getTicketTypes
- theme tokens: --tl-background, --tl-border, --tl-border-hover, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-size-2xs, --tl-font-size-sm, --tl-font-size-xs, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| event-id | eventId | string \| undefined | no | The event, for its prices; without it the types the page has already read |
| layout | layout | "dropdown" \| "segmented" | no | A dropdown per seat, or a segmented control with each price |
| occurrence-id | occurrenceId | string | yes | The occurrence the seats belong to |
| seat | seat | number \| undefined | no | One seat, by its index; unset for every seat held for the occurrence |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-price-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | The change could not be made |
| tlPriceChange | { seat: number; ticketTypeId: string; } | A held seat moved to another ticket type |
## tl-promo-code {#element-tl-promo-code}
Apply a code to the current cart: what it unlocks, discounts or waives is that cart's and its order's.
- category: purchase; runtimes: web; docs: https://docs.ticketlayer.com/reference/elements/tl-promo-code
- SDK calls: cart.applyCode
- theme tokens: --tl-background, --tl-border, --tl-border-hover, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-family-mono, --tl-font-size-md, --tl-font-size-sm, --tl-foreground, --tl-input, --tl-muted, --tl-muted-foreground, --tl-radius-full, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-5, --tl-success, --tl-success-foreground
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| button-label | buttonLabel | string | no | Apply button label. |
| collapsible | collapsible | boolean | no | Start as a "Have a code?" link that opens the input when pressed. |
| placeholder | placeholder | string | no | Input placeholder. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-promo-code__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | - |
| tlPromoApplied | TlPromoApplied | - |
## tl-quantity-picker {#element-tl-quantity-picker}
How many tickets: square buttons 1 to 5 and a 6+ dropdown up to the channel's limit, or a keypad for staff. Best available on the same page follows it.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-quantity-picker
- SDK calls: client.emit, client.on, seating.getPointer
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-font-family, --tl-font-family-heading, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| layout | layout | "buttons" \| "keypad" | no | Square buttons and a dropdown, or a keypad (for staff at a counter) |
| max | max | number \| undefined | no | The most that may be chosen, when lower than the channel's limit |
| occurrence-id | occurrenceId | string | yes | The occurrence the tickets are for |
| quantity | quantity | number | no | The number chosen to start with |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-quantity-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlQuantityChange | { quantity: number; } | A number of tickets was chosen |
## tl-refund-request {#element-tl-refund-request}
Ask for a refund on an order, with a reason and where the money should go: back the way it was paid, or as account credit.
- category: account; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-refund-request
- SDK calls: orders.get, orders.requestRefund
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-input, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| allow-credit | allowCredit | boolean | no | Offer account credit as well as a refund the way the order was paid. |
| heading | heading | string | no | The form's heading. |
| order-id | orderId | string | yes | The order to ask about. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-refund-request__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the request cannot be sent, with the message shown. |
| tlRefundRequested | TlRefundRequested | Emitted once the request is sent. |
## tl-seat-card {#element-tl-seat-card}
One seat as a card: the view from it when there are views, the row and number, the section, the price, whether it is free or held, and for a held seat its ticket type to change. The seat explorer pins one beside a tapped seat, never over it. Compact, it says all of that in two lines beside a thumbnail of the view.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-seat-card
- SDK calls: cart.get, cart.on, events.getOffers, seating.getChunk, seating.getSeatmap, seating.getStaffState, seating.getStatus, seating.getView, seating.watchStatus
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-font-family, --tl-font-size-sm, --tl-font-size-xs, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-seat-selected, --tl-seat-selected-mark, --tl-seat-unavailable, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| compact | compact | boolean | no | Two lines and a thumbnail of the view: the seat and price, then the section, category and state on one line. The seat explorer uses it in staff mode, so a pinned card leaves most of the map in view |
| event-id | eventId | string \| undefined | no | The event, for its prices; without it the prices the page has already read |
| occurrence-id | occurrenceId | string | yes | The occurrence the seat belongs to |
| pinned | pinned | boolean | no | Pinned beside a tapped seat: it shows the ticket type and a close button |
| seat | seat | number | no | The seat, by its index in the venue plan version |
| staff | staff | boolean | no | Box office mode, on a client that offers the staff capabilities: the seat says what it is to staff, a hold by its name |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-card__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlClose | void | The buyer closed a pinned card |
| tlViewRequested | { seat: number; } | The buyer asked to see the view from the seat (the picture) |
## tl-seat-explorer {#element-tl-seat-explorer}
The seat map: the venue's sections, flown into one at a time, and its seats in their categories' colours. A tap reserves a seat on its category's default ticket type and pins a card beside it with the type to change; a tap on a held seat gives it back. Keyboard navigation over sections and seats, a list of each section's seats, and every reserve, release and lost seat announced. When the seat map publishes a venue model, a toggle shows the venue in 3D, read only when it opens, with the same seats, taps and keys.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-seat-explorer
- SDK calls: cart.get, cart.on, cart.releaseSeat, cart.reserveSeat, cart.reserveSeats, client.emit, client.on, events.getOffers, seating.getChunk, seating.getMesh, seating.getModel, seating.getSeatmap, seating.getStaffAccess, seating.getStaffState, seating.getViews, seating.watchStatus
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-map-aisle, --tl-map-aisle-line, --tl-map-background, --tl-map-label, --tl-map-label-size, --tl-map-pillar, --tl-map-pillar-edge, --tl-map-pin, --tl-map-pin-active, --tl-map-pin-active-text, --tl-map-pin-border, --tl-map-pin-font-size, --tl-map-pin-height, --tl-map-pin-shadow, --tl-map-pin-text, --tl-map-plate, --tl-map-section, --tl-map-section-outline, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-seat-allocated, --tl-seat-available, --tl-seat-comp, --tl-seat-held, --tl-seat-hit-radius, --tl-seat-hover-ring, --tl-seat-hover-ring-width, --tl-seat-killed, --tl-seat-min-radius, --tl-seat-reserved, --tl-seat-scale, --tl-seat-selected, --tl-seat-selected-mark, --tl-seat-selected-ring, --tl-seat-unavailable, --tl-seat-unavailable-scale, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-stage-edge, --tl-stage-fill, --tl-stage-label, --tl-stage-radius
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| dimension | dimension | "2d" \| "3d" | no | Start on the flat map (`2d`) or in the venue in 3D (`3d`). 3D is offered only when the seat map publishes a venue model, and is read only when it opens; the buyer can switch, and a browser that cannot draw it stays on the flat map |
| event-id | eventId | string | yes | The event, for its prices |
| focus-section | focusSection | string \| undefined | no | Start flown into this section, by its id |
| mode | mode | "list" \| "map" | no | Start on the map, or on the list of each section's seats (the buyer can switch) |
| occurrence-id | occurrenceId | string | yes | The occurrence whose seats to show |
| show-key | showKey | boolean | no | Show the map key (the venue minimap and the price bands) in the corner |
| staff | staff | boolean | no | Box office mode, on a client that offers the staff capabilities: holds, comps, sales, other carts and allocations drawn and counted in the map key's Status tab, kills released on the occurrence drawn as on sale, a compact seat card, a held seat sold after a confirm, a single seat gap sold after a confirm, and no hold countdown |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-explorer__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| view-host | viewHost | "page" \| "self" | no | Where the view from a seat opens: over this map (`self`), or in a `tl-seat-view` the page places over something bigger (`page`), as the buy modal does over its whole panel |
| Event | Detail | When |
|---|---|---|
| tlCartChange | { seats: number[]; } | The seats this buyer holds for the occurrence changed |
| tlError | { message: string; } | The seat map could not be loaded, or a seat could not be written |
| tlRecommendationSelect | { seats: number[]; } | A best available suggestion was taken: its seats are being reserved |
| tlSeatSelect | { seat: number; sectionId: string \| null; selected: boolean; } | A seat was reserved (selected) or given back (not selected) |
| tlSeatView | { seat: number; } | The buyer opened the view from a seat |
| tlSectionFocus | { sectionId: string \| null; sectionName: string \| null; } | The map flew into a section, or back out to the venue (null) |
## tl-seat-preferences {#element-tl-seat-preferences}
What a party prefers beyond how many: a price per ticket, an area, an aisle seat, wheelchair spaces and how together. Folded, it lists only what differs from the default. Best available on the same page follows it.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-seat-preferences
- SDK calls: client.emit, client.on, events.getOffers, seating.getSeatmap
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-font-family, --tl-font-size-sm, --tl-font-size-xs, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-full, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| event-id | eventId | string \| undefined | no | The event, for its prices |
| occurrence-id | occurrenceId | string | yes | The occurrence the preferences are for |
| open | open | boolean | no | Start open rather than folded |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-preferences__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlPreferencesChange | { maxPriceEach: number \| null; areas: string[]; aisle: boolean; accessible: number; together: "row" \| "rows" \| "any"; } | The preferences changed. maxPriceEach is fees included, in minor units |
## tl-seat-view {#element-tl-seat-view}
The view from one seat, or from a block of seats, in a box a little smaller than what it sits over, dimming it: the rendered still and a 360 look around its row where the venue has them, else the nearest rendered seat or a drawing; the distance, angle, how much of the stage is in view, the clearance over the row in front and what is in the way by name; the prices by ticket type; and Select, which holds the seats. The seat card's picture and a best available option's picture open it.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-seat-view
- SDK calls: cart.get, cart.on, cart.reserveSeat, cart.reserveSeats, client.emit, client.on, events.getOffers, seating.getChunk, seating.getMedia, seating.getSeatmap, seating.getStatus, seating.getView, seating.getViewpoints, seating.watchStatus
- theme tokens: --tl-background, --tl-border, --tl-card, --tl-duration-fast, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-family-heading, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xs, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-overlay, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| event-id | eventId | string \| undefined | no | The event, for its prices; without it the prices the page has already read |
| occurrence-id | occurrenceId | string | yes | The occurrence the seats belong to |
| seat | seat | number | no | Open on this seat, by its index in the venue plan version. Without it the box opens when the seat card or best available asks |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-view__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlClose | void | The box closed |
| tlError | { message: string; } | The seats could not be held |
| tlOpen | { seats: number[]; } | The box opened on these seats |
| tlSelect | { seats: number[]; } | The buyer selected the seats and the cart now holds them |
## tl-ticket {#element-tl-ticket}
A single issued ticket: a scannable QR, its name, seat and holder, and a PDF download. Greys out and stamps Used or Void when it can no longer be redeemed.
- category: tickets; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-ticket
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-font-family, --tl-font-family-heading, --tl-font-family-mono, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-font-size-xl, --tl-font-weight-heading, --tl-foreground, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-radius-lg, --tl-radius-md, --tl-radius-sm, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| order-id | orderId | string | yes | Order id this ticket belongs to (for the PDF download). |
| - | ticket | TlTicketData | yes | The ticket to render. |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-ticket__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlDownloadRequested | { ticketId: string; orderId: string; } | Emitted when the buyer asks for this ticket as a file. |
## tl-ticket-selector {#element-tl-ticket-selector}
Pick quantities of the ticket types on sale for one occurrence, priced with every fee in them, and add them to the cart.
- category: purchase; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-ticket-selector
- SDK calls: cart.addItem, cart.addItems, cart.get, cart.on, cart.removeItem, events.getOffers, events.getTicketTypes
- theme tokens: --tl-background, --tl-border, --tl-duration-fast, --tl-duration-normal, --tl-easing-standard, --tl-error, --tl-error-bg, --tl-font-family, --tl-font-size-2xs, --tl-font-size-lg, --tl-font-size-md, --tl-font-size-sm, --tl-foreground, --tl-info, --tl-info-bg, --tl-input, --tl-muted, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-full, --tl-radius-lg, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-spacing-4, --tl-spacing-5, --tl-success, --tl-success-foreground, --tl-warning, --tl-warning-bg
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| button-label | buttonLabel | string | no | Button label |
| event-id | eventId | string | yes | Event ID |
| occurrence-id | occurrenceId | string | yes | Occurrence ID |
| show-button | showButton | boolean | no | Show add to cart button |
| sync-cart | syncCart | boolean | no | Keep the cart in step with the steppers: start from what the cart holds for this occurrence and write each change to it, with no Add button |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-ticket-selector__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlAddToCart | { items: AddToCartItem[]; } | Emitted when items are added to cart |
| tlError | { message: string; } | Emitted on error |
| tlQuantityChange | { ticketTypeId: string; quantity: number; total: number; } | Emitted when quantity changes |
## tl-wristband-activate {#element-tl-wristband-activate}
Activate a wristband that came in the post by tapping it on the phone (Web NFC where the browser has it); where it cannot, explains the desk at the event instead. Says the band is active only once the API does.
- category: tickets; runtimes: web, native; docs: https://docs.ticketlayer.com/reference/elements/tl-wristband-activate
- SDK calls: orders.activateWristband
- theme tokens: --tl-background, --tl-border, --tl-error, --tl-font-family, --tl-font-size-md, --tl-font-size-sm, --tl-font-weight-heading, --tl-foreground, --tl-muted-foreground, --tl-primary, --tl-primary-foreground, --tl-radius-md, --tl-ring, --tl-spacing-1, --tl-spacing-2, --tl-spacing-3, --tl-success
| Attribute | Property | Type | Required | What |
|---|---|---|---|---|
| heading | heading | string | no | A heading in place of "Activate 's wristband". |
| holder-label | holderLabel | string | no | Who the band is for, as the booking names them. |
| order-id | orderId | string | yes | The booking the wristband was posted for. |
| slot-id | slotId | string | yes | The holder it was posted to (`hsl_...`). |
| unstyled | unstyled | boolean | no | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-wristband-activate__*` class. Without it the element is styled, and each node is a `::part()` of the same name. |
| Event | Detail | When |
|---|---|---|
| tlError | { message: string; } | Emitted when the band cannot be read or the API refuses it, with the message shown. |
| tlWristbandActivated | TlWristbandActivated | Emitted once the API says the band is active, never before. |
---
# Elements reference
## Elements
Source: https://docs.ticketlayer.com/reference/elements
Elements are web components (custom HTML tags prefixed `tl-`) that list events,
take a customer through ticket selection, cart and checkout, and show the
tickets they bought. They work in plain HTML, in Webflow or any site builder
that lets you add a script tag, and in React through the generated wrappers in
`@ticketlayer/elements-react`.
Every element binds to the Live client the page publishes on
`window.Ticketlayer`. Elements take no API key of their own: create the client
once (the CDN loader does this from its `data-*` attributes, or call
`createLiveClient()` from `@ticketlayer/live`) and the elements find it.
### Get them onto the page
**CDN, no build step.** One script tag loads the Live SDK, the Elements bundle
and the hosted checkout modal. The tags below then just work.
```html
```
See the [HTML quickstart](https://docs.ticketlayer.com/build/quickstarts/html) for the attributes each
environment needs today.
**npm, for React and bundlers.**
```bash
npm install @ticketlayer/live @ticketlayer/elements @ticketlayer/elements-react
```
```tsx
import { createLiveClient } from '@ticketlayer/live';
import { defineCustomElements, TlEventList } from '@ticketlayer/elements-react';
defineCustomElements(); // once, after hydration
await createLiveClient({ baseUrl: 'live.ticketlayer.com', publishableKey: 'tlpk_...' });
console.log(e.detail.eventId)} />
```
Note (Package status):
`@ticketlayer/live`, `@ticketlayer/elements` and `@ticketlayer/elements-react`
are built in their repositories but are not on the public npm registry yet;
they publish once their release workflows run. Until then use the CDN build,
which is served at `cdn.ticketlayer.com`. See the
[Vite quickstart](https://docs.ticketlayer.com/build/quickstarts/vite-react) for the workaround in the meantime.
### Elements
### Discovery
List and present events.
| Element | React | What it does |
|---|---|---|
| [``](./tl-event-card) | `TlEventCard` | One event as a card: its image, name, next date, venue, what a ticket costs with fees, and how it is selling. |
| [``](./tl-event-list) | `TlEventList` | What the channel is selling, as a grid of event cards, optionally narrowed to one category. |
| [``](./tl-occurrence-selector) | `TlOccurrenceSelector` | A smart occurrence selector component that fetches and displays event occurrences from the Ticketlayer API. |
### Purchase
Choose a date, pick tickets, open the buy flow.
| Element | React | What it does |
|---|---|---|
| [``](./tl-best-available) | `TlBestAvailable` | The best seats for a party: a few options that differ (the best, a cheaper one, one closer or further back), each with the view from it, the row and seats, why it was chosen and the price. |
| [``](./tl-buy-modal) | `TlBuyModal` | The buy-tickets modal: a date, then tickets, then payment, under a header carrying the channel's and the event's identity, beside a basket showing what the buyer is paying for. |
| [``](./tl-buy-tickets-button) | `TlBuyTicketsButton` | A buy-tickets button in one of four sale states, which opens the purchase modal. |
| [``](./tl-buy-tickets-wrapper) | `TlBuyTicketsWrapper` | Wrap any content and open the purchase modal when it is activated. |
| [``](./tl-gift-card-shop) | `TlGiftCardShop` | Buy a gift card: choose the card, an amount it comes in, email or post (post only with a posted order), the recipient and a message, and add it to the basket. |
| [``](./tl-map-key) | `TlMapKey` | The seat map's key: a minimap of the whole venue with the part the map shows outlined (choose a section to go there), and the price bands, each category's colour and lowest price, with the seat states |
| [``](./tl-price-picker) | `TlPricePicker` | The ticket type of a held seat: Adult, Child, Concession and the rest of its category's types, as a dropdown or a segmented control, for one seat or every seat held for the occurrence. |
| [``](./tl-promo-code) | `TlPromoCode` | Apply a code to the current cart: what it unlocks, discounts or waives is that cart's and its order's. |
| [``](./tl-quantity-picker) | `TlQuantityPicker` | How many tickets: square buttons 1 to 5 and a 6+ dropdown up to the channel's limit, or a keypad for staff. |
| [``](./tl-seat-card) | `TlSeatCard` | One seat as a card: the view from it when there are views, the row and number, the section, the price, whether it is free or held, and for a held seat its ticket type to change. |
| [``](./tl-seat-explorer) | `TlSeatExplorer` | The seat map: the venue's sections, flown into one at a time, and its seats in their categories' colours. |
| [``](./tl-seat-preferences) | `TlSeatPreferences` | What a party prefers beyond how many: a price per ticket, an area, an aisle seat, wheelchair spaces and how together. |
| [``](./tl-seat-view) | `TlSeatView` | The view from one seat, or from a block of seats, in a box a little smaller than what it sits over, dimming it: the rendered still and a 360 look around its row where the venue has them, else the near |
| [``](./tl-ticket-selector) | `TlTicketSelector` | Pick quantities of the ticket types on sale for one occurrence, priced with every fee in them, and add them to the cart. |
### Cart
Show and edit what the customer is about to buy.
| Element | React | What it does |
|---|---|---|
| [``](./tl-cart) | `TlCart` | The buyer's cart: its lines, the money rows the surface reported and a checkout button. |
| [``](./tl-cart-badge) | `TlCartBadge` | A cart icon carrying the number of items in the cart, kept current from the cart bus. |
| [``](./tl-cart-drawer) | `TlCartDrawer` | A slide-in panel holding the cart, opened by the cart intent on the client bus. |
### Checkout
Hand off to the hosted checkout and confirm the order.
| Element | React | What it does |
|---|---|---|
| [``](./tl-checkout-credit) | `TlCheckoutCredit` | The checkout's gift cards and credit: apply the signed-in buyer's account credit, or a gift card by its code, see what each covers, and remove either before paying. |
| [``](./tl-checkout-holders) | `TlCheckoutHolders` | The checkout's "Who is this for?" section: a name for each guest, vehicle or group the cart makes, and who each add-on goes to, "one each" when that is automatic. |
| [``](./tl-checkout-summary) | `TlCheckoutSummary` | The checkout's order summary, priced from the cart's quote: the tickets with the fees included in them, each separate fee, any discount and the total checkout charges. |
| [``](./tl-checkout-wrapper) | `TlCheckoutWrapper` | Wrap any content and open the checkout modal when it is activated. |
| [``](./tl-delivery-picker) | `TlDeliveryPicker` | The checkout's Delivery section: how the tickets reach the buyer, each method with what it adds, and the reason beside one that cannot be chosen. |
| [``](./tl-order-confirmation) | `TlOrderConfirmation` | The thank-you block: order reference, email, event and date, the ticket lines, the money rows and the tickets. |
| [``](./tl-payment-picker) | `TlPaymentPicker` | The checkout's Payment section: the ways this order can be paid, each with any surcharge it adds, and the reason beside one that cannot be chosen. |
### Tickets
Render issued tickets with QR codes and PDF downloads.
| Element | React | What it does |
|---|---|---|
| [``](./tl-booking-pass) | `TlBookingPass` | The booking pass: one barcode for the whole order, listing everyone it covers until each has their own pass or wristband. |
| [``](./tl-holder-pass) | `TlHolderPass` | A named guest's own page, from the link the booker sent: their ticket and extras, their name under the event's rules, and their own pass to download. |
| [``](./tl-order-tickets) | `TlOrderTickets` | Every ticket in an order as a scannable card, waiting out issuance rather than reporting an empty order as empty. |
| [``](./tl-ticket) | `TlTicket` | A single issued ticket: a scannable QR, its name, seat and holder, and a PDF download. |
| [``](./tl-wristband-activate) | `TlWristbandActivate` | Activate a wristband that came in the post by tapping it on the phone (Web NFC where the browser has it); where it cannot, explains the desk at the event instead. |
### Account
Sign in with a magic link and show past orders.
| Element | React | What it does |
|---|---|---|
| [``](./tl-booking-holders) | `TlBookingHolders` | Manage my booking, people first: each holder with their ticket and add-ons, to name or rename, move an add-on to someone else, place add-ons nobody has yet, and activate a posted wristband. |
| [``](./tl-gift-card-balance) | `TlGiftCardBalance` | Check what is on a gift card by its code: the amount, until when, and whether it can be used. |
| [``](./tl-login) | `TlLogin` | Magic-link sign-in: an email in, a link or a six-digit code back, and a session. |
| [``](./tl-my-credit) | `TlMyCredit` | Your credit: the signed-in buyer's account credit with this organisation, each balance with its expiry and whether it is on hold, and its activity (what was put on it and spent, when, and on which ord |
| [``](./tl-my-orders) | `TlMyOrders` | The signed-in customer's orders: event, date, ticket count, total and status, each row expanding to its tickets. |
| [``](./tl-refund-request) | `TlRefundRequest` | Ask for a refund on an order, with a reason and where the money should go: back the way it was paid, or as account credit. |
### Other
| Element | React | What it does |
|---|---|---|
| [``](./tl-concierge) | `TlConcierge` | Concierge: search the help articles, ask the AI when the channel has it on, see my orders after a code typed into the widget, or contact the organiser. |
| [``](./tl-concierge-slot) | `TlConciergeSlot` | The "Need help?" another element hosts: renders nothing unless the channel has Concierge on for that placement, then a help field that opens tl-concierge inline with the host's context and what was ty |
### Events
Every element emits `CustomEvent`s whose names start with `tl` and whose
payload is on `event.detail`. In HTML:
```html
```
In React each event is a prop named `onTl...`:
` ...} />`.
### Theming
Elements read CSS custom properties prefixed `--tl-`. The Live SDK injects the
values of the channel's theme (configured in Backstage) when
`applyTheme` is on, which it is by default; set any token yourself on
`:root` or on a wrapper to override. The defaults below come from the
Elements global stylesheet and apply when nothing else sets the token.
### Colour
Palette.
| Token | Default |
|---|---|
| `--tl-primary` | `#4f46e5` |
| `--tl-color-primary` | `var(--tl-primary)` |
| `--tl-primary-foreground` | `#ffffff` |
| `--tl-color-primary-foreground` | `var(--tl-primary-foreground)` |
| `--tl-secondary` | `#f3f4f6` |
| `--tl-color-secondary` | `var(--tl-secondary)` |
| `--tl-secondary-foreground` | `#111827` |
| `--tl-color-secondary-foreground` | `var(--tl-secondary-foreground)` |
| `--tl-accent` | `#ec4899` |
| `--tl-color-accent` | `var(--tl-accent)` |
| `--tl-accent-foreground` | `#111827` |
| `--tl-color-accent-foreground` | `var(--tl-accent-foreground)` |
| `--tl-background` | `#ffffff` |
| `--tl-color-background` | `var(--tl-background)` |
| `--tl-foreground` | `#111827` |
| `--tl-color-foreground` | `var(--tl-foreground)` |
| `--tl-card` | `var(--tl-background)` |
| `--tl-color-card` | `var(--tl-card)` |
| `--tl-muted` | `#f9fafb` |
| `--tl-color-muted` | `var(--tl-muted)` |
| `--tl-muted-foreground` | `#6b7280` |
| `--tl-color-muted-foreground` | `var(--tl-muted-foreground)` |
| `--tl-border` | `#e5e7eb` |
| `--tl-color-border` | `var(--tl-border)` |
| `--tl-border-hover` | `color-mix(in srgb, var(--tl-foreground) 35%, var(--tl-border))` |
| `--tl-color-border-hover` | `var(--tl-border-hover)` |
| `--tl-input` | `#e5e7eb` |
| `--tl-color-input` | `var(--tl-input)` |
| `--tl-ring` | `#4f46e5` |
| `--tl-color-ring` | `var(--tl-ring)` |
| `--tl-success` | `#10b981` |
| `--tl-color-success` | `var(--tl-success)` |
| `--tl-success-foreground` | `#111827` |
| `--tl-color-success-foreground` | `var(--tl-success-foreground)` |
| `--tl-warning` | `#f59e0b` |
| `--tl-color-warning` | `var(--tl-warning)` |
| `--tl-warning-foreground` | `#111827` |
| `--tl-color-warning-foreground` | `var(--tl-warning-foreground)` |
| `--tl-warning-bg` | `color-mix(in srgb, var(--tl-warning) 15%, var(--tl-background))` |
| `--tl-color-warning-bg` | `var(--tl-warning-bg)` |
| `--tl-error` | `#dc2626` |
| `--tl-color-error` | `var(--tl-error)` |
| `--tl-destructive` | `var(--tl-error)` |
| `--tl-color-destructive` | `var(--tl-error)` |
| `--tl-error-foreground` | `#ffffff` |
| `--tl-color-error-foreground` | `var(--tl-error-foreground)` |
| `--tl-destructive-foreground` | `var(--tl-error-foreground)` |
| `--tl-color-destructive-foreground` | `var(--tl-error-foreground)` |
| `--tl-error-bg` | `color-mix(in srgb, var(--tl-error) 12%, var(--tl-background))` |
| `--tl-color-error-bg` | `var(--tl-error-bg)` |
| `--tl-destructive-bg` | `var(--tl-error-bg)` |
| `--tl-color-destructive-bg` | `var(--tl-error-bg)` |
| `--tl-info` | `#3b82f6` |
| `--tl-color-info` | `var(--tl-info)` |
| `--tl-info-foreground` | `#ffffff` |
| `--tl-color-info-foreground` | `var(--tl-info-foreground)` |
| `--tl-info-bg` | `color-mix(in srgb, var(--tl-info) 14%, var(--tl-background))` |
| `--tl-color-info-bg` | `var(--tl-info-bg)` |
| `--tl-overlay` | `color-mix(in srgb, color-mix(in srgb, var(--tl-background) 22%, #000000) 55%, transparent)` |
### Type
Font stacks, the base size and ratio, and the type scale derived from them.
| Token | Default |
|---|---|
| `--tl-font-family` | `system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif` |
| `--tl-font-family-heading` | `var(--tl-font-family)` |
| `--tl-font-weight-heading` | `600` |
| `--tl-font-family-mono` | `ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace` |
| `--tl-font-size-base` | `16px` |
| `--tl-base-size` | `var(--tl-font-size-base)` |
| `--tl-font-scale` | `1.25` |
| `--tl-font-size-2xs` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), -1.5))` |
| `--tl-font-size-xs` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), -1))` |
| `--tl-font-size-sm` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), -0.5))` |
| `--tl-font-size-md` | `var(--tl-font-size-base)` |
| `--tl-font-size-lg` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), 0.5))` |
| `--tl-font-size-xl` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), 1))` |
| `--tl-font-size-2xl` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), 2))` |
| `--tl-font-size-3xl` | `calc(var(--tl-font-size-base) * pow(var(--tl-font-scale), 3))` |
### Radius
Corner radii.
| Token | Default |
|---|---|
| `--tl-radius-none` | `0px` |
| `--tl-radius-sm` | `0.25rem` |
| `--tl-radius-md` | `0.5rem` |
| `--tl-radius` | `var(--tl-radius-md)` |
| `--tl-radius-lg` | `0.75rem` |
| `--tl-radius-full` | `9999px` |
### Spacing
The spacing step scale.
| Token | Default |
|---|---|
| `--tl-spacing-1` | `0.25rem` |
| `--tl-spacing-2` | `0.5rem` |
| `--tl-spacing-3` | `0.75rem` |
| `--tl-spacing-4` | `1rem` |
| `--tl-spacing-5` | `1.25rem` |
| `--tl-spacing-6` | `1.5rem` |
| `--tl-spacing-8` | `2rem` |
### Motion
Durations and easings.
| Token | Default |
|---|---|
| `--tl-duration-fast` | `120ms` |
| `--tl-duration-normal` | `200ms` |
| `--tl-duration-slow` | `320ms` |
| `--tl-easing-standard` | `cubic-bezier(0.2, 0, 0, 1)` |
| `--tl-easing-emphasised` | `cubic-bezier(0.3, 0, 0, 1)` |
### Seat map
The seat map: the map itself (map-\*: background, plates, sections, labels, pins), the stage (stage-\*) and the seats (seat-\*: each state, the selected seat on the brand colour, the hover ring and the sizes they are drawn at).
| Token | Default |
|---|---|
| `--tl-map-background` | `#f4f4f5` |
| `--tl-map-plate` | `#ffffff` |
| `--tl-map-section` | `#ececef` |
| `--tl-map-section-outline` | `#a1a1aa` |
| `--tl-stage-fill` | `#e4e4e7` |
| `--tl-stage-edge` | `#d4d4d8` |
| `--tl-stage-label` | `#52525b` |
| `--tl-map-label` | `#52525b` |
| `--tl-map-pillar` | `#a1a1aa` |
| `--tl-map-pillar-edge` | `#71717a` |
| `--tl-map-aisle` | `#fafafa` |
| `--tl-map-aisle-line` | `#a1a1aa` |
| `--tl-seat-available` | `var(--tl-primary)` |
| `--tl-seat-unavailable` | `#d4d4d8` |
| `--tl-seat-killed` | `#e4e4e7` |
| `--tl-seat-held` | `#d97706` |
| `--tl-seat-comp` | `#0d9488` |
| `--tl-seat-allocated` | `#a21caf` |
| `--tl-seat-reserved` | `#a1a1aa` |
| `--tl-seat-selected` | `var(--tl-primary)` |
| `--tl-seat-selected-mark` | `var(--tl-primary-foreground)` |
| `--tl-seat-selected-ring` | `var(--tl-foreground)` |
| `--tl-seat-hover-ring` | `var(--tl-foreground)` |
| `--tl-map-pin` | `#ffffff` |
| `--tl-map-pin-border` | `#e4e4e7` |
| `--tl-map-pin-text` | `var(--tl-foreground)` |
| `--tl-map-pin-active` | `var(--tl-foreground)` |
| `--tl-map-pin-active-text` | `var(--tl-background)` |
| `--tl-map-pin-shadow` | `#09090b2e` |
| `--tl-seat-scale` | `0.4` |
| `--tl-seat-unavailable-scale` | `0.6` |
| `--tl-seat-min-radius` | `1.2px` |
| `--tl-seat-hit-radius` | `3px` |
| `--tl-seat-hover-ring-width` | `1.5px` |
| `--tl-stage-radius` | `9px` |
| `--tl-map-pin-height` | `28px` |
| `--tl-map-label-size` | `var(--tl-font-size-xs)` |
| `--tl-map-pin-font-size` | `var(--tl-font-size-xs)` |
```css
:root {
--tl-primary: #0f766e;
--tl-primary-foreground: #ffffff;
--tl-radius-md: 0.25rem;
--tl-font-family: 'Inter', system-ui, sans-serif;
}
```
Elements render in shadow DOM, so ordinary CSS selectors from the page do not
reach inside them; the tokens are the styling contract.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-best-available
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBestAvailable } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
The best seats for a party: a few options that differ (the best, a cheaper
one, one closer or further back), each with the view from it, the row and
seats, why it was chosen and the price. Hovering an option rings its seats
on `tl-seat-explorer`; taking it holds them and pins the seat card. "How
many tickets?" and the preferences sit above the options unless turned off,
and follow the same choices wherever else they are placed on the page.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `eventId` | `event-id` | The event, for its prices | `string \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence to find seats in | `string` | `undefined` |
| `options` | `options` | How many options to show | `number` | `3` |
| `quantity` | `quantity` | How many tickets to start with | `number` | `2` |
| `showPreferences` | `show-preferences` | Show the preferences under "How many tickets?" | `boolean` | `true` |
| `showQuantity` | `show-quantity` | Show "How many tickets?" above the options | `boolean` | `true` |
| `staff` | `staff` | Box office mode: one option at a time with "Next best", a keypad for how many, the channel's own number of options, and a single seat gap sold after a confirm where the client allows it | `boolean` | `false` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-best-available__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------------- | -------------------------------------------------------------- | -------------------------------------------------- |
| `tlError` | Suggestions could not be found, or an option could not be held | `CustomEvent<{ message: string; }>` |
| `tlOptionSelect` | An option was taken and its seats are held | `CustomEvent<{ seats: number[]; facet: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-quantity-picker), [``](./tl-seat-preferences)
Used by: [``](./tl-buy-modal)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-booking-holders
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBookingHolders } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
Manage my booking, people first: each holder with their ticket and add-ons,
to name or rename, move an add-on to someone else, place add-ons nobody has
yet, and activate a posted wristband. The event's pass policy decides what
can change, its naming rules are on each card, and its refusals are said in
plain words on the card.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | --------------- |
| `allowActivation` | `allow-activation` | Offer to activate a wristband that came in the post, on a holder whose posted band waits for them. | `boolean` | `true` |
| `guestPageUrl` | `guest-page-url` | The page a guest's link opens, the one carrying `tl-holder-pass`; the link adds `order`, `slot` and `holder_token` to it. Without it, this page without its query. | `string` | `''` |
| `heading` | `heading` | The element's heading. | `string` | `"Who's going"` |
| `orderId` _(required)_ | `order-id` | The booking to manage. | `string` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-booking-holders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| -------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when a change is refused or the booking cannot be loaded, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlGuestLinkCreated` | Emitted when the booker makes a guest's own link, with the link and when it expires. | `CustomEvent` |
| `tlHoldersChanged` | Emitted after a holder is named or an add-on is moved or placed. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-wristband-activate)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-booking-pass
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBookingPass } from '@ticketlayer/elements-react'` |
| Category | Tickets |
| Runs in | web (browser DOM), native (planned React Native set) |
The booking pass: one barcode for the whole order, listing everyone it
covers until each has their own pass or wristband. The number is written
under the code, so the barcode is never the only way to read it.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------- |
| `heading` | `heading` | The element's heading. | `string` | `'Your booking pass'` |
| `orderId` _(required)_ | `order-id` | The booking whose pass to show. | `string` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-booking-pass__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------- | ----------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the booking pass cannot be loaded. | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-buy-modal
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBuyModal } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM) |
The buy-tickets modal: a date, then tickets, then payment, under a header
carrying the channel's and the event's identity, beside a basket showing
what the buyer is paying for. The hosted checkout opens inside it where the
page allows.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------- | ----------- |
| `channelHeader` | `channel-header` | Whether and how the channel's logo and name are drawn above the event: hidden, over the blurred image, or on the primary colour. Defaults to the channel theme's design.modalHeader.channel, then hidden | `"blurred" \| "hidden" \| "plain" \| undefined` | `undefined` |
| `eventId` _(required)_ | `event-id` | Event to sell tickets for | `string` | `undefined` |
| `header` | `header` | How the event band is drawn: the event image blurred behind a square of it, or plain on the background. Defaults to the channel theme's design.modalHeader.event, then blurred | `"blurred" \| "plain" \| undefined` | `undefined` |
| `occurrenceId` | `occurrence-id` | Optionally open straight to ticket selection for this occurrence | `string \| undefined` | `undefined` |
| `paymentReturn` | `payment-return` | A payment method coming back to the page: open straight on payment to finish it. Set by | `string \| undefined` | `undefined` |
| `size` | `size` | A fixed 1040 by 640 frame that keeps its size through every step, or nearly the whole screen. Full screen on a phone either way | `"fixed" \| "wide"` | `'fixed'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-buy-modal__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------ | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `tlCheckout` | The tickets are in the cart and the host's own checkout takes over (the page cannot show the checkout inside the modal) | `CustomEvent` |
| `tlClose` | The buyer dismissed the modal | `CustomEvent` |
| `tlComplete` | The buyer paid, inside the modal; the modal shows its confirmation | `CustomEvent<{ order: unknown; }>` |
| `tlError` | The event or its tickets couldn't be loaded, the tickets couldn't be added, or the checkout couldn't be opened | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-occurrence-selector), [``](./tl-ticket-selector), [``](./tl-seat-explorer), [``](./tl-best-available), [``](./tl-cart), [``](./tl-promo-code), [``](./tl-concierge-slot), [``](./tl-seat-view)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-buy-tickets-button
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBuyTicketsButton } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM) |
A buy-tickets button in one of four sale states, which opens the purchase
modal.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------- |
| `currency` | `currency` | Currency code for price formatting | `string` | `'GBP'` |
| `disabled` | `disabled` | Disabled state | `boolean` | `false` |
| `eventId` _(required)_ | `event-id` | Event ID to purchase tickets for | `string` | `undefined` |
| `fullWidth` | `full-width` | Full width button | `boolean` | `false` |
| `label` | `label` | Button label override (default depends on state) | `string \| undefined` | `undefined` |
| `occurrenceId` | `occurrence-id` | Optional pre-selected occurrence ID | `string \| undefined` | `undefined` |
| `price` | `price` | Price to display (in minor units e.g. pence) | `number \| undefined` | `undefined` |
| `pricePrefix` | `price-prefix` | Show price prefix text | `string` | `'From'` |
| `size` | `size` | Button size | `"lg" \| "md" \| "sm"` | `'md'` |
| `state` | `state` | Button state - determines colour, label, and icon | `"available" \| "comingSoon" \| "soldOut" \| "waitlist"` | `'available'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-buy-tickets-button__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
| `variant` | `variant` | Button variant | `"outline" \| "primary" \| "secondary"` | `'primary'` |
### Events
| Event | Description | Type |
| --------- | --------------------------------------------------- | ----------------------------------------------------------------------- |
| `tlClick` | Emitted when button is clicked (before modal opens) | `CustomEvent<{ eventId: string; occurrenceId?: string \| undefined; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-buy-tickets-wrapper
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlBuyTicketsWrapper } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM) |
Wrap any content and open the purchase modal when it is activated.
Wraps any content and opens the Ticketlayer modal when clicked.
Uses the SDK's modal manager which handles the iframe overlay.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | --------------- | ----------------------------------------------------------- | --------------------- | ----------- |
| `disabled` | `disabled` | Whether the wrapper is disabled | `boolean` | `false` |
| `eventId` _(required)_ | `event-id` | The event ID to purchase tickets for | `string` | `undefined` |
| `occurrenceId` | `occurrence-id` | Optional specific occurrence ID (skips occurrence selector) | `string \| undefined` | `undefined` |
### Events
| Event | Description | Type |
| ------------ | -------------------------------------------- | ----------------------------------- |
| `tlClose` | Emitted when the modal closes | `CustomEvent` |
| `tlComplete` | Emitted when purchase completes successfully | `CustomEvent` |
| `tlError` | Emitted when an error occurs | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Slots
| Slot | Description |
| ---- | --------------------------------------- |
| | Content to display (typically a button) |
##
Source: https://docs.ticketlayer.com/reference/elements/tl-cart-badge
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCartBadge } from '@ticketlayer/elements-react'` |
| Category | Cart |
| Runs in | web (browser DOM) |
A cart icon carrying the number of items in the cart, kept current from the
cart bus.
### Properties
| Property | Attribute | Description | Type | Default |
| ----------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ----------- |
| `badgeOnly` | `badge-only` | Badge only mode - only shows the count bubble, no icon or button | `boolean` | `false` |
| `icon` | `icon` | Custom icon (emoji or text). If not provided, uses cart SVG | `string \| undefined` | `undefined` |
| `showEmpty` | `show-empty` | Show the badge even when count is 0 | `boolean` | `false` |
| `size` | `size` | Size variant | `"lg" \| "md" \| "sm"` | `'md'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart-badge__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------- | --------------------------------- | ------------------------------------- |
| `tlClick` | Emitted when the badge is clicked | `CustomEvent<{ itemCount: number; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-cart-drawer
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCartDrawer } from '@ticketlayer/elements-react'` |
| Category | Cart |
| Runs in | web (browser DOM) |
A slide-in panel holding the cart, opened by the cart intent on the client
bus.
A slide-in cart panel (off-canvas) showing the current cart. It opens when
anything raises the `ui:cart-requested` bus intent - e.g. wire a
`tl-cart-badge`'s click to emit it - and closes on the backdrop, the close
button, Escape, or `ui:modal-close-requested`. The panel embeds ``,
so the actual cart rendering / remove / checkout is shared with the inline
cart; the drawer only adds the off-canvas chrome.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------- |
| `heading` | `heading` | Heading shown at the top of the panel. | `string` | `'Your cart'` |
| `open` | `open` | Controlled open state (also driven by the `ui:cart-requested` intent). | `boolean` | `false` |
| `side` | `side` | Edge the panel slides in from. | `"left" \| "right"` | `'right'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart-drawer__*` class. Without it the element is styled, and each node is a `::part()` of the same name. The cart inside the panel follows the same mode. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------- | ----------- | ------------------- |
| `tlClose` | | `CustomEvent` |
| `tlOpen` | | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-cart)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-cart
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCart } from '@ticketlayer/elements-react'` |
| Category | Cart |
| Runs in | web (browser DOM), native (planned React Native set) |
The buyer's cart: its lines, the money rows the surface reported and a
checkout button.
### Properties
| Property | Attribute | Description | Type | Default |
| -------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------ |
| `checkoutLabel` | `checkout-label` | Checkout button label | `string` | `'Checkout'` |
| `compact` | `compact` | Compact mode - hides some details | `boolean` | `false` |
| `showCheckoutButton` | `show-checkout-button` | Show checkout button | `boolean` | `true` |
| `showRemoveButtons` | `show-remove-buttons` | Show remove item buttons | `boolean` | `true` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-cart__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------- | -------------------------------------- | ------------------------------------------------------ |
| `tlCartUpdated` | Emitted when cart is updated | `CustomEvent<{ cart: TlCartData; }>` |
| `tlCheckout` | Emitted when checkout is clicked | `CustomEvent<{ cart: TlCartData; }>` |
| `tlError` | Emitted on error | `CustomEvent<{ message: string; }>` |
| `tlItemRemoved` | Emitted when item is removed | `CustomEvent<{ itemId: string; }>` |
| `tlSeatRemoved` | Emitted when a held seat is given back | `CustomEvent<{ occurrenceId: string; seat: number; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-price-picker)
Used by: [``](./tl-buy-modal), [``](./tl-cart-drawer)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-checkout-credit
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCheckoutCredit } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The checkout's gift cards and credit: apply the signed-in buyer's account
credit, or a gift card by its code, see what each covers, and remove either
before paying. The rest is paid by the payment method chosen below. The
rest is paid by the method chosen in `tl-payment-picker`.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ------------------------- |
| `heading` | `heading` | The section's heading. | `string` | `'Gift cards and credit'` |
| `showCredit` | `show-credit` | Offer the signed-in buyer's account credit, when they have some in this order's currency. | `boolean` | `true` |
| `showGiftCards` | `show-gift-cards` | Offer paying with a gift card by its code. | `boolean` | `true` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-credit__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | --------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when a gift card or credit cannot be applied, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlTendersChanged` | Emitted when gift cards or credit are applied or removed: what they pay and what is left. Never carries a code. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-checkout-holders
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCheckoutHolders } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The checkout's "Who is this for?" section: a name for each guest, vehicle
or group the cart makes, and who each add-on goes to, "one each" when that
is automatic. Every change is saved at once and a refusal is said beside
the add-on it is about. Renders nothing when the cart has nobody to name.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------------------- |
| `heading` | `heading` | The section's heading. | `string` | `'Who is this for?'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-holders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------------- | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when a change is refused or the holders cannot be loaded, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlHoldersSaved` | Emitted after names or add-on choices are saved, with how many are named and how many add-ons are still to place. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-checkout-summary
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCheckoutSummary } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The checkout's order summary, priced from the cart's quote: the tickets
with the fees included in them, each separate fee, any discount and the
total checkout charges. It re-prices as delivery and payment are chosen. It
re-prices as `tl-delivery-picker` and `tl-payment-picker` choose, says it
is updating rather than show a stale total, and announces the new total
politely.
Gift cards being bought are a row of their own, and gift cards or credit
applied to pay (`tl-checkout-credit`) are listed under the total with what
each covers, then what is left to pay (P9).
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------- |
| `heading` | `heading` | The summary's heading. | `string` | `'Order summary'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-checkout-summary__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------------- | -------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the quote cannot be read. | `CustomEvent<{ message: string; }>` |
| `tlQuoteUpdated` | Emitted when the quote moves to a new total. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-checkout-wrapper
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlCheckoutWrapper } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM) |
Wrap any content and open the checkout modal when it is activated.
Wraps any content and opens the Ticketlayer checkout modal when clicked.
Uses the SDK's modal manager which handles the iframe overlay.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------- | --------- | ------- |
| `disabled` | `disabled` | Whether the wrapper is disabled | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------ | -------------------------------------------- | ----------------------------------- |
| `tlClose` | Emitted when the modal closes | `CustomEvent` |
| `tlComplete` | Emitted when checkout completes successfully | `CustomEvent` |
| `tlError` | Emitted when an error occurs | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Slots
| Slot | Description |
| ---- | ------------------------------------------------ |
| | Content to display (typically a checkout button) |
##
Source: https://docs.ticketlayer.com/reference/elements/tl-concierge-slot
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlConciergeSlot } from '@ticketlayer/elements-react'` |
| Category | support |
| Runs in | web (browser DOM), native (planned React Native set) |
The "Need help?" another element hosts: renders nothing unless the channel
has Concierge on for that placement, then a help field that opens
tl-concierge inline with the host's context and what was typed. The
buy-tickets modal, the checkout step, my-orders and the order confirmation
each place one with what they know (event, occurrence, cart, order). It
renders nothing - no box, no gap - unless the channel's Concierge config is
on with that placement switched on; then the help field: a "Need help?"
heading that opens the panel, the host's context as a chip, and one field
that searches (and asks the persona, when the AI is on). Sending the field
opens tl-concierge inline with the host's context, already searching for
what was typed.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------------------ | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | -------------- |
| `cartId` | `cart-id` | Context: the cart. | `string \| undefined` | `undefined` |
| `eventId` | `event-id` | Context: the event. | `string \| undefined` | `undefined` |
| `label` | `label` | The text of the toggle. | `string` | `'Need help?'` |
| `occurrenceId` | `occurrence-id` | Context: the occurrence. | `string \| undefined` | `undefined` |
| `orderId` | `order-id` | Context: the order. | `string \| undefined` | `undefined` |
| `placement` _(required)_ | `placement` | Which element hosts it; the channel switches each placement on separately. | `"buy-modal" \| "checkout" \| "confirmation" \| "my-orders"` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-concierge-slot__*` class. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------- | -------------------------------- | --------------------------------- |
| `tlConciergeToggle` | The help panel opened or closed. | `CustomEvent<{ open: boolean; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-concierge)
Used by: [``](./tl-buy-modal), [``](./tl-my-orders), [``](./tl-order-confirmation)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-concierge
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlConcierge } from '@ticketlayer/elements-react'` |
| Category | support |
| Runs in | web (browser DOM), native (planned React Native set) |
Concierge: search the help articles, ask the AI when the channel has it on,
see my orders after a code typed into the widget, or contact the organiser.
Hidden unless the channel turns it on. A launcher bubble, an inline panel
or a full page (HBO `/help`); inside another element through
tl-concierge-slot.
Laid out as a front desk: a home with the persona's greeting in a band of
the brand's colour, one field that searches as the customer types (and
offers to ask the persona when the query reads as a question), and labelled
doors to Ask, My orders and Contact; every other view has a bar with a way
back; a tab bar keeps the four in reach, at the thumb on a launcher and
across the top of a page.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| -------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ------------ |
| `cartId` | `cart-id` | Context: the cart, when the host has one. | `string \| undefined` | `undefined` |
| `embedded` | `embedded` | Set by tl-concierge-slot: the element it is inside, whose placement the channel switches on. | `"buy-modal" \| "checkout" \| "confirmation" \| "my-orders" \| undefined` | `undefined` |
| `eventId` | `event-id` | Context: the event the customer is looking at. | `string \| undefined` | `undefined` |
| `occurrenceId` | `occurrence-id` | Context: the occurrence the customer is looking at. | `string \| undefined` | `undefined` |
| `open` | `open` | Launcher only: start with the panel open. | `boolean` | `false` |
| `orderId` | `order-id` | Context: the order the customer is looking at. | `string \| undefined` | `undefined` |
| `ordersView` | `orders-view` | After verification: hand off to tl-my-orders (element), or list the orders in the widget (list). | `"element" \| "list"` | `'element'` |
| `placement` | `placement` | Where it sits: a launcher bubble, an inline panel, or a full page. | `"inline" \| "launcher" \| "page"` | `'launcher'` |
| `query` | `query` | A search to start with, shown in the field: what the customer typed into the embedded field. | `string \| undefined` | `undefined` |
| `section` | `section` | The tab to open on, when the channel offers it. | `"ask" \| "contact" \| "orders" \| "search" \| undefined` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-concierge__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tlConciergeCardAction` | The customer answered a confirm card the engine sent: confirmed (executed or failed, with the code) or cancelled. | `CustomEvent<{ proposal: string; proposalId: string; action: "cancel" \| "confirm"; outcome: "failed" \| "cancelled" \| "executed"; code: string \| null; }>` |
| `tlConciergeContactSent` | The contact form opened a case. | `CustomEvent<{ caseId: string \| null; reference: string \| null; }>` |
| `tlConciergeHandover` | The assistant handed the conversation to a person. | `CustomEvent<{ caseId: string \| null; }>` |
| `tlConciergeToggle` | The launcher panel opened or closed. | `CustomEvent<{ open: boolean; }>` |
| `tlConciergeVerified` | The code was accepted and the customer is signed in. | `CustomEvent<{ authenticated: boolean; }>` |
| `tlError` | Something the customer asked for failed. | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-my-orders)
Used by: [``](./tl-concierge-slot)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-delivery-picker
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlDeliveryPicker } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The checkout's Delivery section: how the tickets reach the buyer, each
method with what it adds, and the reason beside one that cannot be chosen.
Choosing re-prices the cart's quote. While email is the one method the
section answers itself and folds to "Email, Free". Choosing writes it to
the cart (`PUT /sales/carts/{cartId}/choices`) and `tl-checkout-summary`
re-prices from the quote.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------- | ------------ |
| `autoChoose` | `auto-choose` | Choose the only method that can be chosen when nothing is chosen yet, so the quote includes it. | `boolean` | `true` |
| `collapse` | `collapse` | Fold the section to one line when its only method is chosen, with Change when there is more to see. | `boolean` | `true` |
| `heading` | `heading` | The section's heading. | `string` | `'Delivery'` |
| `unavailable` | `unavailable` | Which methods that cannot be chosen are shown, disabled with the reason: `relevant` (closed by a cut-off, business customers only), `all`, or `none`. | `"all" \| "none" \| "relevant"` | `'relevant'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-delivery-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | ------------------------------------------------------------------- | --------------------------------------------------------- |
| `tlDeliveryChosen` | Emitted once a delivery method is chosen, with the re-priced total. | `CustomEvent<{ method: string; total: number \| null; }>` |
| `tlError` | Emitted when a choice is refused or the options cannot be read. | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-event-card
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlEventCard } from '@ticketlayer/elements-react'` |
| Category | Discovery |
| Runs in | web (browser DOM), native (planned React Native set) |
One event as a card: its image, name, next date, venue, what a ticket costs
with fees, and how it is selling.
From the Live listing and event views' offers the card shows what
one ticket costs with every fee in it, as a range (part `price`), the
event's state for this buyer (`badge`, `locked` included), and a countdown
to a sale that opens later (`countdown`). On an older response the price is
the face value, labelled "From".
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----------- |
| `event` | -- | Event data to display (tl-event-list passes this) | `TlEventCardEvent \| undefined` | `undefined` |
| `eventId` | `event-id` | Load the event from the SDK by id when no `event` is given, so a single card works standalone (e.g. on a Webflow page). | `string \| undefined` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-event-card__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------- | ------------------------------------------------ | ----------------------------------- |
| `tlClick` | Emitted when the card is clicked | `CustomEvent<{ eventId: string; }>` |
| `tlError` | Emitted when the event could not be loaded by id | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-event-list)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-event-list
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlEventList } from '@ticketlayer/elements-react'` |
| Category | Discovery |
| Runs in | web (browser DOM), native (planned React Native set) |
What the channel is selling, as a grid of event cards, optionally narrowed
to one category.
tl-event-list - what the channel is selling, as a grid of tl-event-card.
Each card shows the listing's fee-inclusive price range and its state for
this buyer, with a countdown when the sale opens later; see
`tl-event-card`.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `category` | `category` | Category filter | `string \| undefined` | `undefined` |
| `columns` | `columns` | Columns for grid layout (1, 2, 3, or 4) | `number` | `3` |
| `limit` | `limit` | Maximum number of events to display | `number` | `12` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-event-list__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| -------------- | ------------------------------------------ | ----------------------------------- |
| `tlError` | Emitted when the listing could not be read | `CustomEvent<{ message: string; }>` |
| `tlEventClick` | Emitted when an event card is clicked | `CustomEvent<{ eventId: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-event-card)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-gift-card-balance
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlGiftCardBalance } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
Check what is on a gift card by its code: the amount, until when, and
whether it can be used. Too many tries in a minute are met with a friendly
wait. The code goes in the request body, the field is emptied once the card
is found, and the answer names the card by the last four characters of its
code. Too many tries in a minute are met with a friendly wait.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------- |
| `buttonLabel` | `button-label` | The check button label. | `string` | `'Check balance'` |
| `heading` | `heading` | The element's heading. | `string` | `'Check a gift card balance'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-gift-card-balance__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | --------------------------------------------------------------------- | ----------------------------------- |
| `tlBalanceChecked` | Emitted when a card is found: its balance and status, never its code. | `CustomEvent` |
| `tlError` | Emitted when a balance cannot be checked, with the message shown. | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-gift-card-shop
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlGiftCardShop } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
Buy a gift card: choose the card, an amount it comes in, email or post
(post only with a posted order), the recipient and a message, and add it to
the basket. The cards in the basket are listed with a Remove each. The
cards in the basket are listed with a Remove each, and
`tl-checkout-summary` shows what they cost as its own row.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------------- |
| `buttonLabel` | `button-label` | The add button's label; "Add £50.00 gift card to basket" when left empty. | `string` | `''` |
| `heading` | `heading` | The shop's heading. | `string` | `'Gift cards'` |
| `productId` | `product-id` | Show only this gift card (its id); every gift card on sale when left empty. | `string` | `''` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-gift-card-shop__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ----------------- | -------------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the gift cards cannot be loaded or one cannot be added. | `CustomEvent<{ message: string; }>` |
| `tlGiftCardAdded` | Emitted once a gift card is in the basket. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-holder-pass
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlHolderPass } from '@ticketlayer/elements-react'` |
| Category | Tickets |
| Runs in | web (browser DOM), native (planned React Native set) |
A named guest's own page, from the link the booker sent: their ticket and
extras, their name under the event's rules, and their own pass to download.
Nothing else of the booking: no refunds, transfers or moving extras.
Nothing else of the booking: no refunds, transfers or moving extras, which
stay with the booker.
```html
on the page a guest's link opens.
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------- |
| `heading` | `heading` | The element's heading. | `string` | `'Your pass'` |
| `orderId` | `order-id` | The booking; read from the link's `order` when not set. | `string` | `''` |
| `slotId` | `slot-id` | The guest's holder (`hsl_...`); read from the link's `slot` when not set. | `string` | `''` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-holder-pass__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- |
| `tlError` | Emitted when the pass cannot be loaded or a change is refused, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlHolderNamed` | Emitted once the API has saved the name the guest gave. | `CustomEvent` |
| `tlPassDownloadRequested` | The guest asked for their pass as a file. Announced before the harness does anything, whether or not the download goes on to succeed. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-login
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlLogin } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
Magic-link sign-in: an email in, a link or a six-digit code back, and a
session. No password is ever handled.
tl-login - magic-link sign-in. The customer enters an email, we ask the SDK
to send a link (POST /sales/customer-auth/magic-link), then either the link
lands back on this page with `?code=` or the customer types the code from
the email; both exchange through the SDK (POST /sales/customer-tokens). No
password is ever handled here. The link's code is a long token and is sent
on its own; a typed 6-digit code is sent with the email it went to, because
the API refuses a short code without its contact.
States: enter email -> check your inbox (resend, enter code) -> signed in
(email + sign out).
```html
```
### What signing in and out does to the cart
**Signing in keeps the buyer's cart.** When the exchange succeeds, Live
attaches the session's open cart to the customer and re-evaluates every line
for them, so member prices open without starting again. The client takes the
re-evaluated cart and `cart:updated` carries it; a line the customer can no
longer buy is reported (`cartLines`, with its reason) and left in the cart for
them to remove, never silently dropped. If the customer already had another
open cart, the one they were just building is kept and the other expires on
its own; nothing is merged.
**Signing out empties the cart, on purpose.** "Sign out" drops the cart and
releases the tickets it was holding straight away. On a shared device a cart
left behind would still carry the signed-in customer's identity and any
member prices they had unlocked, so the element never leaves one for the next
person. A buyer who signs out mid-purchase starts a new cart.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------- | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `allowCodeEntry` | `allow-code-entry` | Offer a "enter the code" path alongside the emailed link (default true). | `boolean` | `true` |
| `heading` | `heading` | Heading for the email step. | `string` | `'Sign in'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-login__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ----------------- | ----------- | ----------------------------------- |
| `tlAuthChanged` | | `CustomEvent` |
| `tlError` | | `CustomEvent<{ message: string; }>` |
| `tlMagicLinkSent` | | `CustomEvent<{ email: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-my-orders)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-map-key
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlMapKey } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
The seat map's key: a minimap of the whole venue with the part the map shows
outlined (choose a section to go there), and the price bands, each
category's colour and lowest price, with the seat states drawn in shape as
well as colour. In staff mode a Status tab counts the seats in each state
the box office's map draws: available by price category, this sale, each
hold by name, comps, sold, other carts and killed.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ----------- |
| `collapsed` | `collapsed` | Start closed, showing only the tabs | `boolean` | `false` |
| `eventId` | `event-id` | The event, for the prices; without it the prices the page has already read | `string \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence whose seat map this is the key to | `string` | `undefined` |
| `staff` | `staff` | Box office mode, on a client that offers the staff capabilities: a Status tab counting available seats by price category, this sale, each hold and each allocation by name, comps, sold, other carts and killed seats | `boolean` | `false` |
| `tab` | `tab` | The tab to open on: the venue minimap, the price bands, or in staff mode the seat states | `"bands" \| "status" \| "venue"` | `'venue'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-map-key__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | --------------------------------------------------------------------------------------------- | --------------------------------------------- |
| `tlSectionRequest` | The buyer chose a section on the minimap (null: the whole venue); the map is asked to show it | `CustomEvent<{ sectionId: string \| null; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-seat-explorer)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-my-credit
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlMyCredit } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
Your credit: the signed-in buyer's account credit with this organisation,
each balance with its expiry and whether it is on hold, and its activity
(what was put on it and spent, when, and on which order) a page at a time.
A buyer who is not signed in is asked to.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | --------------- |
| `heading` | `heading` | The element's heading. | `string` | `'Your credit'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-my-credit__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------- | ----------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the credit cannot be loaded. | `CustomEvent<{ message: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-my-orders
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlMyOrders } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
The signed-in customer's orders: event, date, ticket count, total and
status, each row expanding to its tickets.
tl-my-orders - the signed-in customer's orders (GET /sales/my-orders through
the SDK): event, date, ticket count, total and status per row. Clicking a
row expands it to show the order's tickets (tl-order-tickets). Shows
tl-login inline when nobody is signed in.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| -------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | -------------------------------------------------------------------------- |
| `columns` | `columns` | Columns for the expanded ticket grid. | `number` | `2` |
| `emptyMessage` | `empty-message` | Copy for the empty state. | `string` | `"When you buy tickets they'll show up here, ready to view and download."` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-my-orders__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------- | ----------- | ----------------------------------- |
| `tlError` | | `CustomEvent<{ message: string; }>` |
| `tlOrderSelect` | | `CustomEvent<{ orderId: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-order-tickets), [``](./tl-concierge-slot), [``](./tl-login)
Used by: [``](./tl-concierge)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-occurrence-selector
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlOccurrenceSelector } from '@ticketlayer/elements-react'` |
| Category | Discovery |
| Runs in | web (browser DOM) |
A smart occurrence selector component that fetches and displays event
occurrences from the Ticketlayer API.
A smart occurrence selector component that fetches and displays event occurrences from the Ticketlayer API.
### Features
- **Automatic data fetching** - Fetches occurrences via the Ticketlayer SDK
- **Adaptive display modes** - Automatically chooses list, slots, or calendar view based on occurrence count
- **Loading & error states** - Built-in spinner and error handling with retry
- **Responsive** - Works on mobile and desktop with auto-adjusting layouts
- **Themeable** - Fully customizable via CSS custom properties
### Usage
```tsx
import { TlOccurrenceSelector } from '@ticketlayer/elements-react';
console.log('Selected:', e.detail.occurrence)}
/>
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | ----------- |
| `displayMode` | `display-mode` | Force a specific display mode (auto-detected if not set) | `"calendar" \| "calendar-slots" \| "dates" \| "list" \| "slots" \| undefined` | `undefined` |
| `eventId` _(required)_ | `event-id` | Event ID to fetch occurrences for | `string` | `undefined` |
| `occurrenceId` | `occurrence-id` | The occurrence already picked: the element opens on its day and marks it | `string \| undefined` | `undefined` |
| `orientation` | `orientation` | Layout orientation (auto = horizontal on desktop, vertical on mobile) | `"auto" \| "horizontal" \| "vertical"` | `'auto'` |
| `theme` | -- | Theme configuration | `TlOccurrenceSelectorTheme \| undefined` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-occurrence-selector__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------- | -------------------------------------- | -------------------------------------------- |
| `tlSelect` | Emitted when an occurrence is selected | `CustomEvent<{ occurrence: TlOccurrence; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-buy-modal)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-order-confirmation
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlOrderConfirmation } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The thank-you block: order reference, email, event and date, the ticket
lines, the money rows and the tickets.
tl-order-confirmation - the "thank you" page block: order reference,
customer email, event and date, ticket lines, totals, then the tickets
themselves (tl-order-tickets, each with a Download PDF link).
Finds its order three ways, in priority order:
1. the `order-id` prop
2. the URL (`?orderId=`), the contract the hosted box office confirmation
page uses; `?redirect_status=` from a Stripe return is honoured too
3. the SDK's `checkout:completed` event, for a page that mounts this before
the customer pays
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `columns` | `columns` | Columns for the ticket grid. | `number` | `2` |
| `customerEmail` | `customer-email` | Email to show when the order view carries none (a host that collected it at checkout). | `string \| undefined` | `undefined` |
| `orderId` | `order-id` | The order to show. Falls back to `?orderId=` in the page URL. | `string \| undefined` | `undefined` |
| `showTickets` | `show-tickets` | Mount tl-order-tickets under the summary (default true). | `boolean` | `true` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-order-confirmation__*` class. Without it the element is styled, and each node is a `::part()` of the same name. The ticket grid inside follows the same mode. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------- | ----------- | -------------------------------------- |
| `tlError` | | `CustomEvent<{ message: string; }>` |
| `tlOrderLoaded` | | `CustomEvent<{ order: TlOrderData; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-order-tickets), [``](./tl-concierge-slot)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-order-tickets
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlOrderTickets } from '@ticketlayer/elements-react'` |
| Category | Tickets |
| Runs in | web (browser DOM), native (planned React Native set) |
Every ticket in an order as a scannable card, waiting out issuance rather
than reporting an empty order as empty.
tl-order-tickets - renders all the tickets for an order as scannable QR
cards with PDF download. Fetches the order through the SDK (order-access
gated by the session), so it works on a confirmation page or in an account.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `columns` | `columns` | Columns in the grid (default 2). | `number` | `2` |
| `orderId` _(required)_ | `order-id` | The order whose tickets to show. | `string` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-order-tickets__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Related elements
Renders: [``](./tl-ticket)
Used by: [``](./tl-my-orders), [``](./tl-order-confirmation)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-payment-picker
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlPaymentPicker } from '@ticketlayer/elements-react'` |
| Category | Checkout |
| Runs in | web (browser DOM), native (planned React Native set) |
The checkout's Payment section: the ways this order can be paid, each with
any surcharge it adds, and the reason beside one that cannot be chosen.
Choosing re-prices the cart's quote. Choosing writes it to the cart (`PUT
/sales/carts/{cartId}/choices`) and `tl-checkout-summary` re-prices from
the quote.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------ |
| `autoChoose` | `auto-choose` | Choose the only method that can be chosen when nothing is chosen yet, so the quote includes it. | `boolean` | `true` |
| `collapse` | `collapse` | Fold the section to one line when its only method is chosen, with Change when there is more to see. | `boolean` | `true` |
| `heading` | `heading` | The section's heading. | `string` | `'Payment'` |
| `unavailable` | `unavailable` | Which methods that cannot be chosen are shown, disabled with the reason: `relevant` (closed by a cut-off, business customers only), `all`, or `none`. | `"all" \| "none" \| "relevant"` | `'relevant'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-payment-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ----------------- | ------------------------------------------------------------------ | --------------------------------------------------------- |
| `tlError` | Emitted when a choice is refused or the options cannot be read. | `CustomEvent<{ message: string; }>` |
| `tlPaymentChosen` | Emitted once a payment method is chosen, with the re-priced total. | `CustomEvent<{ method: string; total: number \| null; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-price-picker
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlPricePicker } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
The ticket type of a held seat: Adult, Child, Concession and the rest of its
category's types, as a dropdown or a segmented control, for one seat or
every seat held for the occurrence. A change keeps the seat.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | ------------ |
| `eventId` | `event-id` | The event, for its prices; without it the types the page has already read | `string \| undefined` | `undefined` |
| `layout` | `layout` | A dropdown per seat, or a segmented control with each price | `"dropdown" \| "segmented"` | `'dropdown'` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence the seats belong to | `string` | `undefined` |
| `seat` | `seat` | One seat, by its index; unset for every seat held for the occurrence | `number \| undefined` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-price-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------- | ---------------------------------------- | ------------------------------------------------------ |
| `tlError` | The change could not be made | `CustomEvent<{ message: string; }>` |
| `tlPriceChange` | A held seat moved to another ticket type | `CustomEvent<{ seat: number; ticketTypeId: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-cart), [``](./tl-seat-card)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-promo-code
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlPromoCode } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM) |
Apply a code to the current cart: what it unlocks, discounts or waives is
that cart's and its order's.
tl-promo-code - an input plus Apply button that applies a code to the
current cart (POST /sales/carts/{cartId}/codes through the SDK's
cart.applyCode). What the code unlocks, discounts or waives is that
cart's and its order's; nothing is granted to the buyer, so a guest can use
one. The SDK publishes cart:updated with the cart the API answered, so
tl-cart and badges re-render on their own.
The applied state offers "use a different code", which clears the input for
another code and leaves the first on the cart.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------------------- |
| `buttonLabel` | `button-label` | Apply button label. | `string` | `'Apply'` |
| `collapsible` | `collapsible` | Start as a "Have a code?" link that opens the input when pressed. | `boolean` | `false` |
| `placeholder` | `placeholder` | Input placeholder. | `string` | `'Promo or presale code'` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-promo-code__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------------- | ----------- | ----------------------------------- |
| `tlError` | | `CustomEvent<{ message: string; }>` |
| `tlPromoApplied` | | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-buy-modal)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-quantity-picker
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlQuantityPicker } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
How many tickets: square buttons 1 to 5 and a 6+ dropdown up to the
channel's limit, or a keypad for staff. Two are chosen to start with, and
`layout="keypad"` gives the keypad, for a counter. `tl-best-available` on
the same page follows the number chosen, wherever each is placed.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | ----------- |
| `layout` | `layout` | Square buttons and a dropdown, or a keypad (for staff at a counter) | `"buttons" \| "keypad"` | `'buttons'` |
| `max` | `max` | The most that may be chosen, when lower than the channel's limit | `number \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence the tickets are for | `string` | `undefined` |
| `quantity` | `quantity` | The number chosen to start with | `number` | `2` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-quantity-picker__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | ------------------------------ | ------------------------------------ |
| `tlQuantityChange` | A number of tickets was chosen | `CustomEvent<{ quantity: number; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-best-available)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-refund-request
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlRefundRequest } from '@ticketlayer/elements-react'` |
| Category | Account |
| Runs in | web (browser DOM), native (planned React Native set) |
Ask for a refund on an order, with a reason and where the money should go:
back the way it was paid, or as account credit. Staff decide the request.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -------------------- |
| `allowCredit` | `allow-credit` | Offer account credit as well as a refund the way the order was paid. | `boolean` | `true` |
| `heading` | `heading` | The form's heading. | `string` | `'Ask for a refund'` |
| `orderId` _(required)_ | `order-id` | The order to ask about. | `string` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-refund-request__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------- | ---------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the request cannot be sent, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlRefundRequested` | Emitted once the request is sent. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
##
Source: https://docs.ticketlayer.com/reference/elements/tl-seat-card
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlSeatCard } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
One seat as a card: the view from it when there are views, the row and
number, the section, the price, whether it is free or held, and for a held
seat its ticket type to change. The seat explorer pins one beside a tapped
seat, never over it. Compact, it says all of that in two lines beside a
thumbnail of the view.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- |
| `compact` | `compact` | Two lines and a thumbnail of the view: the seat and price, then the section, category and state on one line. The seat explorer uses it in staff mode, so a pinned card leaves most of the map in view | `boolean` | `false` |
| `eventId` | `event-id` | The event, for its prices; without it the prices the page has already read | `string \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence the seat belongs to | `string` | `undefined` |
| `pinned` | `pinned` | Pinned beside a tapped seat: it shows the ticket type and a close button | `boolean` | `false` |
| `seat` | `seat` | The seat, by its index in the venue plan version | `number` | `-1` |
| `staff` | `staff` | Box office mode, on a client that offers the staff capabilities: the seat says what it is to staff, a hold by its name | `boolean` | `false` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-card__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ----------------- | ----------------------------------------------------------- | -------------------------------- |
| `tlClose` | The buyer closed a pinned card | `CustomEvent` |
| `tlViewRequested` | The buyer asked to see the view from the seat (the picture) | `CustomEvent<{ seat: number; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-price-picker)
Used by: [``](./tl-seat-explorer)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-seat-explorer
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlSeatExplorer } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
The seat map: the venue's sections, flown into one at a time, and its seats
in their categories' colours. A tap reserves a seat on its category's
default ticket type and pins a card beside it with the type to change; a tap
on a held seat gives it back. Keyboard navigation over sections and seats, a
list of each section's seats, and every reserve, release and lost seat
announced. When the seat map publishes a venue model, a toggle shows the
venue in 3D, read only when it opens, with the same seats, taps and keys.
The seat card and the map key are other Elements placed here as a host
would place them (`tl-seat-card`, `tl-map-key`), each unstyled when this
one is.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `dimension` | `dimension` | Start on the flat map (`2d`) or in the venue in 3D (`3d`). 3D is offered only when the seat map publishes a venue model, and is read only when it opens; the buyer can switch, and a browser that cannot draw it stays on the flat map | `"2d" \| "3d"` | `'2d'` |
| `eventId` _(required)_ | `event-id` | The event, for its prices | `string` | `undefined` |
| `focusSection` | `focus-section` | Start flown into this section, by its id | `string \| undefined` | `undefined` |
| `mode` | `mode` | Start on the map, or on the list of each section's seats (the buyer can switch) | `"list" \| "map"` | `'map'` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence whose seats to show | `string` | `undefined` |
| `showKey` | `show-key` | Show the map key (the venue minimap and the price bands) in the corner | `boolean` | `true` |
| `staff` | `staff` | Box office mode, on a client that offers the staff capabilities: holds, comps, sales, other carts and allocations drawn and counted in the map key's Status tab, kills released on the occurrence drawn as on sale, a compact seat card, a held seat sold after a confirm, a single seat gap sold after a confirm, and no hold countdown | `boolean` | `false` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-explorer__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
| `viewHost` | `view-host` | Where the view from a seat opens: over this map (`self`), or in a `tl-seat-view` the page places over something bigger (`page`), as the buy modal does over its whole panel | `"page" \| "self"` | `'self'` |
### Events
| Event | Description | Type |
| ------------------------ | ------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `tlCartChange` | The seats this buyer holds for the occurrence changed | `CustomEvent<{ seats: number[]; }>` |
| `tlError` | The seat map could not be loaded, or a seat could not be written | `CustomEvent<{ message: string; }>` |
| `tlRecommendationSelect` | A best available suggestion was taken: its seats are being reserved | `CustomEvent<{ seats: number[]; }>` |
| `tlSeatSelect` | A seat was reserved (selected) or given back (not selected) | `CustomEvent<{ seat: number; sectionId: string \| null; selected: boolean; }>` |
| `tlSeatView` | The buyer opened the view from a seat | `CustomEvent<{ seat: number; }>` |
| `tlSectionFocus` | The map flew into a section, or back out to the venue (null) | `CustomEvent<{ sectionId: string \| null; sectionName: string \| null; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Renders: [``](./tl-seat-card), [``](./tl-map-key), [``](./tl-seat-view)
Used by: [``](./tl-buy-modal)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-seat-preferences
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlSeatPreferences } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
What a party prefers beyond how many: a price per ticket, an area, an aisle
seat, wheelchair spaces and how together. Folded, it lists only what
differs from the default, each with a way to remove it. `tl-best-available`
on the same page follows it, wherever each is placed.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ----------- |
| `eventId` | `event-id` | The event, for its prices | `string \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence the preferences are for | `string` | `undefined` |
| `open` | `open` | Start open rather than folded | `boolean` | `false` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-preferences__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `tlPreferencesChange` | The preferences changed. maxPriceEach is fees included, in minor units | `CustomEvent<{ maxPriceEach: number \| null; areas: string[]; aisle: boolean; accessible: number; together: "row" \| "rows" \| "any"; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-best-available)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-seat-view
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlSeatView } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
The view from one seat, or from a block of seats, in a box a little smaller
than what it sits over, dimming it: the rendered still and a 360 look
around its row where the venue has them, else the nearest rendered seat or a
drawing; the distance, angle, how much of the stage is in view, the
clearance over the row in front and what is in the way by name; the prices
by ticket type; and Select, which holds the seats. The seat card's picture
and a best available option's picture open it. Place it inside a positioned
box: it covers that box.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------- | ----------- |
| `eventId` | `event-id` | The event, for its prices; without it the prices the page has already read | `string \| undefined` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | The occurrence the seats belong to | `string` | `undefined` |
| `seat` | `seat` | Open on this seat, by its index in the venue plan version. Without it the box opens when the seat card or best available asks | `number` | `-1` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-seat-view__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------- | -------------------------------------------------------- | ----------------------------------- |
| `tlClose` | The box closed | `CustomEvent` |
| `tlError` | The seats could not be held | `CustomEvent<{ message: string; }>` |
| `tlOpen` | The box opened on these seats | `CustomEvent<{ seats: number[]; }>` |
| `tlSelect` | The buyer selected the seats and the cart now holds them | `CustomEvent<{ seats: number[]; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-buy-modal), [``](./tl-seat-explorer)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-ticket-selector
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlTicketSelector } from '@ticketlayer/elements-react'` |
| Category | Purchase |
| Runs in | web (browser DOM), native (planned React Native set) |
Pick quantities of the ticket types on sale for one occurrence, priced with
every fee in them, and add them to the cart.
What a row shows comes from the Live event view's offers: the
price with every fee in it and the fee lines under it (part `breakdown`),
the state with the buyer's reason (`badge`), "Few left" or "Limited" from
the capacity bucket, the limits per order (`limits`), and, for a locked
price, how to unlock it (`unlock`), with no stepper. An occurrence not on
sale says so above the rows (`notice`), with a countdown to the opening
(`countdown`) that reads the tickets again when it arrives. On an older
response the price is the face value and the rest is as before.
### Properties
| Property | Attribute | Description | Type | Default |
| --------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | --------------- |
| `buttonLabel` | `button-label` | Button label | `string` | `'Add to Cart'` |
| `eventId` _(required)_ | `event-id` | Event ID | `string` | `undefined` |
| `occurrenceId` _(required)_ | `occurrence-id` | Occurrence ID | `string` | `undefined` |
| `showButton` | `show-button` | Show add to cart button | `boolean` | `true` |
| `syncCart` | `sync-cart` | Keep the cart in step with the steppers: start from what the cart holds for this occurrence and write each change to it, with no Add button | `boolean` | `false` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-ticket-selector__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ------------------ | ------------------------------------ | ------------------------------------------------------------------------- |
| `tlAddToCart` | Emitted when items are added to cart | `CustomEvent<{ items: AddToCartItem[]; }>` |
| `tlError` | Emitted on error | `CustomEvent<{ message: string; }>` |
| `tlQuantityChange` | Emitted when quantity changes | `CustomEvent<{ ticketTypeId: string; quantity: number; total: number; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Methods
### `syncToCart() => Promise`
Write any press still waiting to the cart now, rather than a moment after
the last one, and resolve once the cart has it. For a `sync-cart` element
that something else moves on from (`tl-buy-modal`'s Continue).
#### Returns
Type: `Promise`
### Related elements
Used by: [``](./tl-buy-modal)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-ticket
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlTicket } from '@ticketlayer/elements-react'` |
| Category | Tickets |
| Runs in | web (browser DOM), native (planned React Native set) |
A single issued ticket: a scannable QR, its name, seat and holder, and a PDF
download. Greys out and stamps Used or Void when it can no longer be
redeemed.
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ----------- |
| `orderId` _(required)_ | `order-id` | Order id this ticket belongs to (for the PDF download). | `string` | `undefined` |
| `ticket` _(required)_ | -- | The ticket to render. | `TlTicketData` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-ticket__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| --------------------- | ------------------------------------------------------ | ----------------------------------------------------- |
| `tlDownloadRequested` | Emitted when the buyer asks for this ticket as a file. | `CustomEvent<{ ticketId: string; orderId: string; }>` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-order-tickets)
##
Source: https://docs.ticketlayer.com/reference/elements/tl-wristband-activate
| | |
|---|---|
| HTML tag | `` |
| React | `import { TlWristbandActivate } from '@ticketlayer/elements-react'` |
| Category | Tickets |
| Runs in | web (browser DOM), native (planned React Native set) |
Activate a wristband that came in the post by tapping it on the phone (Web
NFC where the browser has it); where it cannot, explains the desk at the
event instead. Says the band is active only once the API does. Reads the
chip with Web NFC where the browser has it (Chrome on Android); everywhere
else it explains the other ways (that phone, or the desk at the event) and
offers nothing that would not work. The band is called active only once the
API says so.
```html
```
### Properties
| Property | Attribute | Description | Type | Default |
| ---------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ----------- |
| `heading` | `heading` | A heading in place of "Activate 's wristband". | `string` | `''` |
| `holderLabel` | `holder-label` | Who the band is for, as the booking names them. | `string` | `''` |
| `orderId` _(required)_ | `order-id` | The booking the wristband was posted for. | `string` | `undefined` |
| `slotId` _(required)_ | `slot-id` | The holder it was posted to (`hsl_...`). | `string` | `undefined` |
| `unstyled` | `unstyled` | Render into the light DOM with no stylesheet, for a page that styles the element itself: every node carries a `tl-wristband-activate__*` class. Without it the element is styled, and each node is a `::part()` of the same name. | `boolean` | `false` |
### Events
| Event | Description | Type |
| ---------------------- | ----------------------------------------------------------------------------------- | ----------------------------------- |
| `tlError` | Emitted when the band cannot be read or the API refuses it, with the message shown. | `CustomEvent<{ message: string; }>` |
| `tlWristbandActivated` | Emitted once the API says the band is active, never before. | `CustomEvent` |
Events are `CustomEvent`s; the payload is on `event.detail`. In HTML, listen with `element.addEventListener('tlXxx', handler)`; in React the wrapper exposes each one as an `onTlXxx` prop.
### Related elements
Used by: [``](./tl-booking-holders)
---
# API operation index
## Backstage API {#ops-backstage-api}
Base URL: https://api.ticketlayer.com/v1 (staging: https://api.staging.t9r.dev/v1). OpenAPI document: https://docs.ticketlayer.com/openapi/backstage.json. Reference pages: https://docs.ticketlayer.com/api/backstage/.
### Identity
- GET /auth/interactions/{uid} - Describe a pending sign-in (getSignInInteraction)
- POST /auth/interactions/{uid}/login - Sign in to a pending authorisation (submitSignInInteractionLogin)
- POST /auth/interactions/{uid}/organisation - Choose the organisation for a pending authorisation (submitSignInInteractionOrganisation)
- POST /auth/interactions/{uid}/consent - Approve a client for a pending authorisation (submitSignInInteractionConsent)
- POST /auth/interactions/{uid}/switch - Change the account or organisation of a pending authorisation (switchSignInInteraction)
- POST /auth/interactions/{uid}/abort - Decline or cancel a pending authorisation (abortSignInInteraction)
- POST /auth/start - Start a first-party sign-in (startSignIn)
- POST /auth/password - Sign in with a password (submitSignInPassword)
- POST /auth/email-code - Sign in with an emailed code (submitSignInEmailCode)
- POST /auth/mfa - Complete a sign-in with a second factor (submitSignInMfa)
- POST /auth/signup/start - Start a sign-up (startSignUp)
- POST /auth/signup/verify - Finish a sign-up (verifySignUp)
- POST /auth/invitations/start - Start accepting an invitation (startInvitationSignIn)
- POST /auth/invitations/accept - Accept an invitation with a new password (acceptInvitationSignIn)
### Meta
- GET /meta/versions - List API versions (listApiVersions)
### Signup
- POST /signup - Sign up (signUp)
### Me
- GET /me - Get current user (getCurrentUser)
- POST /me/invitations/redeem - Accept an invitation as the signed-in person (redeemMyInvitation)
- GET /me/organisations - Get my organisations (getMyOrganisations)
- GET /me/roles - Get my roles (getMyRoles)
- GET /me/permissions - Get my permissions (getMyPermissions)
- GET /me/grants - List my connected apps (listMyGrants)
- DELETE /me/grants/{grantId} - Revoke one of my connected apps (revokeMyGrant)
### Accounts
- POST /accounts - Create account (createAccount)
- GET /accounts - List accounts (listAccounts)
- GET /accounts/{id} - Get account (getAccount)
- PATCH /accounts/{id} - Update account (updateAccount)
- DELETE /accounts/{id} - Delete account (deleteAccount)
### Partners
- POST /partners/accounts - Provision a partner customer account (createPartnerAccount)
### API Keys
- POST /api-keys - Create an organisation API key (createApiKey)
- GET /api-keys - List organisation API keys (listApiKeys)
- GET /api-keys/scopes - List the scopes an organisation API key may hold (listApiKeyScopes)
- GET /api-keys/{id} - Get an organisation API key (getApiKey)
- PATCH /api-keys/{id} - Change an organisation API key (updateApiKey)
- DELETE /api-keys/{id} - Revoke an organisation API key (revokeApiKey)
- POST /api-keys/{id}/roll - Roll an organisation API key (rollApiKey)
### Embed Sessions
- POST /embed/sessions - Mint an embed session (createEmbedSession)
- GET /embed/sessions/current - Describe the current embed session (getCurrentEmbedSession)
### User Invitations
- POST /users/invitations - Create user invitation (createUserInvitation)
- GET /users/invitations - List user invitations (listUserInvitations)
- GET /users/invitations/{id} - Get user invitation (getUserInvitation)
- DELETE /users/invitations/{id} - Cancel user invitation (cancelUserInvitation)
- POST /users/invitations/{id}/resend - Resend user invitation (resendUserInvitation)
### Users
- POST /users - Create user (createUser)
- GET /users - List users (listUsers)
- GET /users/{id} - Get user (getUser)
- PATCH /users/{id} - Update user (updateUser)
- DELETE /users/{id} - Delete user (deleteUser)
- POST /users/{usrId}/roles - Assign a role to a user (addRoleToUser)
- DELETE /users/{usrId}/roles/{rolId} - Remove a role from a user (removeRoleFromUser)
### Roles
- POST /roles - Create role (createRole)
- GET /roles - List roles (listRoles)
- GET /roles/{id} - Get role (getRole)
- PATCH /roles/{id} - Update role (updateRole)
- DELETE /roles/{id} - Delete role (deleteRole)
### User Account Roles
- GET /user-accounts - List all role assignments (listUserAccountRoles)
- POST /user-accounts - Assign role to user (assignRole)
- PUT /user-accounts - Update user roles on account (updateUserAccountRoles)
- DELETE /user-accounts/{id} - Remove role assignment (removeRoleAssignment)
- GET /user-accounts/user/{userId} - List role assignments for user (listUserAccountRolesByUser)
- GET /user-accounts/account/{accountId} - List role assignments for account (listUserAccountRolesByAccount)
### Organisations
- GET /organisations/{id} - Get organisation (getOrganisation)
- PATCH /organisations/{id} - Update organisation (updateOrganisation)
- POST /organisations/{id}/graduate - Graduate the organisation from test mode to live (graduateOrganisation)
### Plan
- GET /plan - Get the organisation's plan (getPlan)
### Identity Providers
- GET /identity-providers - List identity providers (listIdentityProviders)
- GET /identity-providers/{id} - Get identity provider (getIdentityProvider)
- PATCH /identity-providers/{id} - Update identity provider (updateIdentityProvider)
### Events
- POST /events - Create event (createEvent)
- GET /events - List events (listEvents)
- GET /events/{id} - Get event (getEvent)
- PATCH /events/{id} - Update event (updateEvent)
- DELETE /events/{id} - Delete event (deleteEvent)
- GET /events/{id}/history - Get event history (getEventHistory)
- GET /events/{id}/notes - List event notes (listEventNotes)
- POST /events/{id}/notes - Add event note (createEventNote)
- PATCH /events/{id}/notes/{noteId} - Edit event note (updateEventNote)
- DELETE /events/{id}/notes/{noteId} - Delete event note (deleteEventNote)
- GET /events/{eventId}/layout - Get event layout (getEventLayout)
- POST /events/{eventId}/layout/validate - Validate layout changes (validateEventLayoutChanges)
- POST /events/{eventId}/layout/apply - Apply layout changes (applyEventLayoutChanges)
- GET /events/{eventId}/layout/apply/{workflowId} - Get layout change result (getEventLayoutChangeResult)
- GET /events/{eventId}/layout/apply/{workflowId}/progress - Stream layout change progress (streamEventLayoutChangeProgress)
### Event Offers
- GET /events/{eventId}/offers - Get event offers (getEventOffers)
### Event Occurrences
- POST /events/{eventId}/occurrences - Create event occurrence (createEventOccurrence)
- GET /events/{eventId}/occurrences - List occurrences for event (listEventOccurrences)
- GET /events/{eventId}/occurrences/{occurrenceId} - Get event occurrence (getEventOccurrence)
- PATCH /events/{eventId}/occurrences/{occurrenceId} - Update event occurrence (updateEventOccurrence)
- DELETE /events/{eventId}/occurrences/{occurrenceId} - Delete event occurrence (deleteEventOccurrence)
### Event Price Schemes
- POST /events/{eventId}/price-schemes - Create price scheme for event (createEventPriceScheme)
- GET /events/{eventId}/price-schemes - List price schemes for event (listEventPriceSchemes)
- GET /events/{eventId}/price-schemes/{priceSchemeId} - Get price scheme (getEventPriceScheme)
- PATCH /events/{eventId}/price-schemes/{priceSchemeId} - Update price scheme (updateEventPriceScheme)
- DELETE /events/{eventId}/price-schemes/{priceSchemeId} - Delete price scheme (deleteEventPriceScheme)
### Ticket Type Configs
- GET /events/{eventId}/ticket-type-configs - An event's ticket type setup (listTicketTypeConfigs)
- GET /events/{eventId}/ticket-type-configs/{ticketTypeId} - A ticket type's setup (getTicketTypeConfig)
- PUT /events/{eventId}/ticket-type-configs/{ticketTypeId} - Set a ticket type's setup (putTicketTypeConfig)
### Venues
- POST /venues - Create venue (createVenue)
- GET /venues - List venues (listVenues)
- GET /venues/{id} - Get venue (getVenue)
- PATCH /venues/{id} - Update venue (updateVenue)
- DELETE /venues/{id} - Delete venue (deleteVenue)
### Venue Layouts
- POST /venues/{id}/layouts - Create venue layout (createVenueLayout)
- GET /venues/{id}/layouts - List layouts for venue (listVenueLayouts)
- GET /venues/{id}/layouts/{layoutId} - Get venue layout (getVenueLayout)
- PATCH /venues/{id}/layouts/{layoutId} - Update venue layout (updateVenueLayout)
- DELETE /venues/{id}/layouts/{layoutId} - Delete venue layout (deleteVenueLayout)
### Venue Layout Areas
- POST /venues/{id}/layouts/{layoutId}/areas - Add area to venue layout (addAreaToVenueLayout)
- PATCH /venues/{id}/layouts/{layoutId}/areas/{areaId} - Update venue layout area (updateVenueLayoutArea)
- DELETE /venues/{id}/layouts/{layoutId}/areas/{areaId} - Delete venue layout area (deleteVenueLayoutArea)
### Venue Layout Categories
- POST /venues/{id}/layouts/{layoutId}/categories - Add category to venue layout (addCategoryToVenueLayout)
- PATCH /venues/{id}/layouts/{layoutId}/categories/{categoryId} - Update venue layout category (updateVenueLayoutCategory)
- DELETE /venues/{id}/layouts/{layoutId}/categories/{categoryId} - Delete venue layout category (deleteVenueLayoutCategory)
### Venue Layout Seats
- POST /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats - Add seat to area (addSeatToArea)
- POST /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats/bulk - Bulk add seats to area (bulkAddSeatsToArea)
- POST /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats/generate - Generate seats for area (generateSeatsForArea)
- PUT /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats/sync - Sync seats in area (syncSeatsInArea)
- PATCH /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats/{seatId} - Update seat in area (updateSeatInArea)
- DELETE /venues/{id}/layouts/{layoutId}/areas/{areaId}/seats/{seatId} - Delete seat from area (deleteSeatFromArea)
### Venue Plans
- GET /venues/{id}/plans - List a venue's seated plans (listVenuePlans)
- POST /venues/{id}/plans - Create a venue plan (createVenuePlan)
- GET /venues/{id}/plans/{planId} - Get a venue plan (getVenuePlan)
- PATCH /venues/{id}/plans/{planId} - Update a venue plan (updateVenuePlan)
- DELETE /venues/{id}/plans/{planId} - Archive a venue plan (archiveVenuePlan)
### Venue Plan Versions
- GET /venues/{id}/plans/{planId}/versions - List a venue plan's versions (listVenuePlanVersions)
- POST /venues/{id}/plans/{planId}/versions - Create a draft version (createVenuePlanVersion)
- GET /venues/{id}/plans/{planId}/versions/{versionId} - Get a venue plan version (getVenuePlanVersion)
- PATCH /venues/{id}/plans/{planId}/versions/{versionId} - Edit a draft version (updateVenuePlanVersion)
- DELETE /venues/{id}/plans/{planId}/versions/{versionId} - Delete a draft version (deleteVenuePlanVersion)
- GET /venues/{id}/plans/{planId}/versions/{versionId}/seats - Get a version's seat table (getVenuePlanSeats)
- PUT /venues/{id}/plans/{planId}/versions/{versionId}/seats - Upsert a draft's seats in bulk (upsertVenuePlanSeats)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/generate - Generate a draft's seats from a description (generateVenuePlanSeats)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/import-csv - Import a seat list into a draft (importVenuePlanCsv)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/publish - Publish a draft version (publishVenuePlanVersion)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/live - Mark a version live (markVenuePlanVersionLive)
- GET /venues/{id}/plans/{planId}/versions/{versionId}/seatmap - Get a version's published seat map (getVenuePlanVersionSeatmap)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/seatmap - Publish a version's seat map again (publishVenuePlanVersionSeatmap)
- PUT /venues/{id}/plans/{planId}/versions/{versionId}/model - Set a version's 3D model and anchor seats (setVenuePlanVersionModel)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/pipeline - Send a version to the 3D pipeline (sendVenuePlanVersionToPipeline)
- GET /venues/{id}/plans/{planId}/versions/{versionId}/ranking - Get a version's ranking (getVenuePlanVersionRanking)
- PUT /venues/{id}/plans/{planId}/versions/{versionId}/ranking - Save a version's draft ranking (saveVenuePlanVersionRankingDraft)
- POST /venues/{id}/plans/{planId}/versions/{versionId}/ranking/sign-off - Sign a version's ranking off (signOffVenuePlanVersionRanking)
### Event Plans
- GET /events/{eventId}/event-plan - Get the event's plan (getEventPlan)
- POST /events/{eventId}/event-plan - Create the event plan (createEventPlan)
- PATCH /events/{eventId}/event-plan - Update the event plan (updateEventPlan)
- PUT /events/{eventId}/event-plan/seat-categories - Set seat categories in bulk (setEventPlanSeatCategories)
- POST /events/{eventId}/event-plan/kills - Kill seats for production (addEventPlanKills)
- POST /events/{eventId}/event-plan/kills/lift - Lift production kills (liftEventPlanKills)
### Billing
- GET /billing/packages - List credit packages (listCreditPackages)
- GET /billing/ai/packages - List AI credit packages (listAiCreditPackages)
- GET /organisations/{organisationId}/billing/can-sell - Check if organisation can sell (checkCanSell)
- GET /billing/balance - Get credit balance (getCreditBalance)
- GET /billing/transactions - List credit transactions (listCreditTransactions)
- POST /billing/purchases - Purchase credits (createCreditPurchase)
- GET /billing/ai/balance - Get AI credit balance (getAiBalance)
- GET /billing/ai/transactions - List AI credit transactions (listAiCreditTransactions)
- POST /billing/ai-topup - Top up AI credits (createAiTopup)
- GET /billing/invoices - List invoices (listInvoices)
- GET /billing/invoices/{invoiceId} - Get invoice (getInvoice)
- GET /billing/payments - List billing payments (listBillingPayments)
- POST /billing/subscriptions - Create subscription (createSubscription)
- GET /billing/subscriptions - List subscriptions (listSubscriptions)
- DELETE /billing/subscriptions/{subscriptionId} - Cancel subscription (cancelSubscription)
- GET /billing/auto-topup - Get auto top-up configuration (getAutoTopup)
- PUT /billing/auto-topup - Update auto top-up configuration (updateAutoTopup)
- GET /billing/payment-methods - List payment methods (listPaymentMethods)
- POST /billing/payment-methods - Save payment method (savePaymentMethod)
- POST /billing/setup-intent - Create setup intent (createSetupIntent)
- DELETE /billing/payment-methods/{paymentMethodId} - Delete payment method (deletePaymentMethod)
- PUT /billing/payment-methods/{paymentMethodId}/default - Set default payment method (setDefaultPaymentMethod)
### Inventory
- POST /inventory - Create inventory (createInventory)
- GET /inventory - List inventory (listInventory)
- GET /inventory/{id} - Get inventory (getInventory)
- DELETE /inventory/{id} - Delete inventory (deleteInventory)
- PATCH /inventory/{id}/capacity - Update inventory capacity (updateInventoryCapacity)
- GET /inventory/{id}/audit-log - Get inventory audit log (getInventoryAuditLog)
### Inventory Seats
- POST /inventory/{id}/seats - Bulk import seats (bulkImportSeats)
- GET /inventory/{id}/seats - List inventory seats (listInventorySeats)
### Inventory Reservations
- POST /inventory/reserve - Reserve inventory (reserveInventory)
- POST /inventory/check-availability - Check availability (checkAvailability)
- GET /inventory/reservations - List reservations (listReservations)
- GET /inventory/reservations/{id} - Get reservation (getReservation)
- POST /inventory/reservations/{id}/confirm - Confirm reservation (confirmReservation)
- POST /inventory/reservations/{id}/release - Release reservation (releaseReservation)
- PATCH /inventory/reservations/{id}/extend - Extend reservation (extendReservation)
### Inventory Holds
- POST /inventory/holds - Create hold (createHold)
- POST /inventory/holds/{id}/release - Release hold (releaseHold)
### Occurrence Seating
- GET /occurrences/{occurrenceId}/seating/state - Get the staff seat state (getOccurrenceSeatingState)
- GET /occurrences/{occurrenceId}/seating/holds - List an occurrence's seat holds (listOccurrenceSeatHolds)
- POST /occurrences/{occurrenceId}/seating/holds - Hold seats under a name (createOccurrenceSeatHold)
- POST /occurrences/{occurrenceId}/seating/holds/{holdId}/release - Release held seats (releaseOccurrenceSeatHold)
- GET /occurrences/{occurrenceId}/seating/allocations - List an occurrence's seat allocations (listOccurrenceSeatAllocations)
- POST /occurrences/{occurrenceId}/seating/allocations - Allocate seats to an agent or a promoter (createOccurrenceSeatAllocation)
- PATCH /occurrences/{occurrenceId}/seating/allocations/{allocationId} - Change a seat allocation (updateOccurrenceSeatAllocation)
- POST /occurrences/{occurrenceId}/seating/allocations/{allocationId}/release - Release allocated seats (releaseOccurrenceSeatAllocation)
- POST /occurrences/{occurrenceId}/seating/kills/release - Release production kills for sale on this occurrence (releaseOccurrenceKills)
- POST /occurrences/{occurrenceId}/seating/kills/restore - Restore released kills on this occurrence (restoreOccurrenceKills)
- GET /occurrences/{occurrenceId}/seatmap - Get an occurrence's seat map pointer (staff) (getOccurrenceSeatmap)
- POST /occurrences/{occurrenceId}/seatmap/tokens - Mint a staff seat map token (createOccurrenceSeatmapToken)
- POST /occurrences/{occurrenceId}/best-available - Find the best available seats (staff) (getOccurrenceBestAvailable)
### Customers
- POST /customers - Create customer (createCustomer)
- GET /customers - List customers (listCustomers)
- POST /customers/match - Match a customer (matchCustomer)
- POST /customers/{customerId}/correct - Correct a customer (correctCustomer)
- GET /customers/{customerId} - Get customer (getCustomer)
- PATCH /customers/{customerId} - Update customer (updateCustomer)
- GET /customers/{customerId}/history - Get customer history (getCustomerHistory)
- GET /customers/{customerId}/notes - List customer notes (listCustomerNotes)
- POST /customers/{customerId}/notes - Add customer note (createCustomerNote)
- PATCH /customers/{customerId}/notes/{noteId} - Edit customer note (updateCustomerNote)
- DELETE /customers/{customerId}/notes/{noteId} - Delete customer note (deleteCustomerNote)
- POST /customers/{customerId}/block - Block customer (blockCustomer)
- POST /customers/{customerId}/unblock - Unblock customer (unblockCustomer)
- POST /customers/{customerId}/erase - Erase customer (eraseCustomer)
- GET /customers/{customerId}/tags - List customer tags (listCustomerTags)
- POST /customers/{customerId}/tags - Grant a customer tag (grantCustomerTag)
- POST /customers/{customerId}/tags/{customerTagId}/revoke - Revoke a customer tag (revokeCustomerTag)
- GET /customers/{customerId}/photo - Get the customer's photo (getCustomerPhoto)
- PUT /customers/{customerId}/photo - Upload the customer's photo (setCustomerPhoto)
- DELETE /customers/{customerId}/photo - Delete the customer's photo (deleteCustomerPhoto)
- POST /customers/{customerId}/activities - Log a call, visit or note on a customer (logCustomerActivity)
- GET /customers/{customerId}/timeline - Get a customer timeline (getCustomerTimeline)
### Refund Policies
- GET /refund-policies - List refund policies (listRefundPolicies)
- PUT /refund-policies - Create or update a refund policy (upsertRefundPolicy)
### Refund Requests
- GET /refund-requests - List refund requests (listRefundRequests)
- POST /refund-requests - Create a refund request (createRefundRequest)
- POST /refund-requests/{requestId}/approve - Approve a refund request (approveRefundRequest)
- POST /refund-requests/{requestId}/process - Process an approved refund request (processRefundRequest)
- PATCH /refund-requests/{requestId} - Change where a refund request pays out (updateRefundRequest)
- POST /refund-requests/{requestId}/reject - Reject a refund request (rejectRefundRequest)
### Orders
- POST /orders - Create order (createOrder)
- GET /orders - List orders (listOrders)
- GET /orders/{orderId} - Get order (getOrder)
- PATCH /orders/{orderId} - Update order (updateOrder)
- GET /orders/{orderId}/history - Get order history (getOrderHistory)
- GET /orders/{orderId}/notes - List order notes (listOrderNotes)
- POST /orders/{orderId}/notes - Add order note (createOrderNote)
- PATCH /orders/{orderId}/notes/{noteId} - Edit order note (updateOrderNote)
- DELETE /orders/{orderId}/notes/{noteId} - Delete order note (deleteOrderNote)
- GET /orders/number/{orderNumber} - Get order by number (getOrderByNumber)
- GET /orders/{orderId}/passes - Get order passes (getOrderPasses)
- GET /orders/{orderId}/passes/{passId}/ticket.pdf - Download a pass as a PDF ticket (getOrderPassPdf)
- POST /orders/{orderId}/resend-confirmation - Resend order confirmation email (resendOrderConfirmation)
- POST /orders/{orderId}/cancel - Cancel order (cancelOrder)
- POST /orders/{orderId}/status - Transition order status (transitionOrderStatus)
- POST /orders/{orderId}/validate - Validate order (validateOrder)
- POST /orders/{orderId}/items - Add line item (addOrderLineItem)
- PATCH /orders/{orderId}/items/{itemId} - Update line item (updateOrderLineItem)
- DELETE /orders/{orderId}/items/{itemId} - Remove line item (removeOrderLineItem)
- POST /orders/{orderId}/payment - Initiate payment (initiateOrderPayment)
- GET /orders/{orderId}/payment/status - Get payment status (getOrderPaymentStatus)
- POST /orders/{orderId}/external-payments - Record payment the channel collected itself (recordExternalOrderPayment)
- POST /orders/{orderId}/recorded-payments - Record a payment staff received (recordOrderPayment)
- POST /orders/{orderId}/awaiting-payment - Place an order awaiting payment (placeOrderAwaitingPayment)
- GET /orders/{orderId}/payment-terms - Get the terms of an order awaiting payment (getOrderPaymentTerms)
- POST /orders/{orderId}/send-now - Send tickets before payment (sendOrderBeforePayment)
- GET /orders/{orderId}/payment-methods - What an order can be paid with (getOrderPaymentMethods)
- POST /orders/{orderId}/fee-lines - Add a one-off fee line (addOrderFeeLine)
- DELETE /orders/{orderId}/fee-lines/{lineItemId} - Remove a one-off fee line (removeOrderFeeLine)
- GET /orders/{orderId}/fulfilment - How an order is being delivered (getOrderFulfilment)
- GET /orders/{orderId}/ship-to - An order's ship-to address (getOrderShipTo)
- PUT /orders/{orderId}/ship-to - Set an order's ship-to address (setOrderShipTo)
- POST /orders/{orderId}/holders/{slotId}/activate - Activate a holder's posted wristband (activateHolderWristband)
- GET /orders/{orderId}/stored-value - The customer's credit that can pay this order (getOrderStoredValue)
- POST /orders/{orderId}/stored-value-payments - Pay with a gift card or account credit (spendOrderStoredValue)
- POST /orders/{orderId}/gift-cards - Sell a gift card on an order (addOrderGiftCard)
- DELETE /orders/{orderId}/gift-cards/{storedValueAccountId} - Take a gift card off an unpaid order (removeOrderGiftCard)
- POST /orders/{orderId}/passes/{passId}/print - Print a pass to a device (printPass)
- GET /orders/{orderId}/holders - An order's holders and add-ons (getOrderHolders)
- PUT /orders/{orderId}/holders/{slotId} - Name a holder (nameOrderHolder)
- POST /orders/{orderId}/add-ons/assign - Put an add-on on a holder (assignOrderAddOn)
### Terminal
- POST /orders/{orderId}/terminal-payments - Charge an order on a terminal reader (chargeOrderOnTerminal)
- GET /orders/{orderId}/terminal-payments/{terminalPaymentId} - Get a terminal payment (getTerminalPayment)
- POST /orders/{orderId}/terminal-payments/{terminalPaymentId}/cancel - Cancel a terminal payment (cancelTerminalPayment)
- POST /terminal/readers - Register a terminal reader (registerTerminalReader)
- GET /terminal/readers - List terminal readers (listTerminalReaders)
- GET /terminal/readers/{readerId} - Get a terminal reader (getTerminalReader)
- DELETE /terminal/readers/{readerId} - Remove a terminal reader (removeTerminalReader)
- POST /terminal/readers/{readerId}/present-payment-method - Present a test card on a simulated reader (presentTerminalTestPaymentMethod)
### Payments
- POST /payments - Create payment (createPayment)
- GET /payments - List payments (listPayments)
- GET /payments/{paymentId} - Get payment (getPayment)
- POST /payments/{paymentId}/cancel - Cancel payment (cancelPayment)
- POST /payments/{paymentId}/refunds - Create refund (createPaymentRefund)
- GET /payments/{paymentId}/refunds - List refunds (listPaymentRefunds)
- POST /payments/{paymentId}/sandbox/confirm - Confirm a sandbox payment (confirmSandboxPayment)
- POST /payments/{paymentId}/paypal/capture - Capture an approved PayPal payment (capturePaypalPayment)
- POST /payments/sandbox/payouts - Pay out a sandbox balance (raiseSandboxPayouts)
### Payment Providers
- POST /payment-providers/ticketlayer-pay/enable - Enable Ticketlayer Pay (enableTicketlayerPay)
- GET /payment-providers/ticketlayer-pay - Ticketlayer Pay status (getTicketlayerPay)
- POST /payment-providers/ticketlayer-pay/onboarding-session - Account Session for embedded Ticketlayer Pay onboarding (createTicketlayerPayOnboardingSession)
- POST /payment-providers/ticketlayer-pay/onboarding-link - Hosted onboarding link for Ticketlayer Pay (createTicketlayerPayOnboardingLink)
- POST /payment-providers/stripe-connect - Link the organisation's own Stripe account (linkStripeConnect)
- GET /payment-providers/stripe-connect - The organisation's Stripe Connect provider (getStripeConnect)
- POST /payment-providers/paypal - Connect the organisation's own PayPal REST app (connectPaypal)
- GET /payment-providers/paypal - The organisation's PayPal connections (listPaypalConnections)
- GET /payment-providers/{providerId}/accounts - Per-Account routing rows on a Stripe Connect provider (listPaymentProviderAccounts)
- GET /payment-providers/{providerId}/accounts/{accountId} - One Account's routing on a Stripe Connect provider (getPaymentProviderAccount)
- PUT /payment-providers/{providerId}/accounts/{accountId} - Set how an Account charges through a Stripe Connect provider (setPaymentProviderAccountMode)
- POST /payment-providers/{providerId}/accounts/{accountId}/onboarding-session - Account Session for embedded onboarding of an Account's connected account (createPaymentProviderAccountOnboardingSession)
- POST /payment-providers/{providerId}/accounts/{accountId}/onboarding-link - Hosted onboarding link for an Account's connected account (createPaymentProviderAccountOnboardingLink)
- POST /payment-providers/adyen/connect - Connect the organisation's own Adyen account (connectAdyen)
- GET /payment-providers/adyen/{providerId} - An Adyen connection (getAdyenConnection)
- POST /payment-providers - Create payment provider (createPaymentProvider)
- GET /payment-providers - List payment providers (listPaymentProviders)
- GET /payment-providers/default - Get default payment provider (getDefaultPaymentProvider)
- GET /payment-providers/{providerId} - Get payment provider (getPaymentProvider)
- PATCH /payment-providers/{providerId} - Update payment provider (updatePaymentProvider)
- DELETE /payment-providers/{providerId} - Delete payment provider (deletePaymentProvider)
- POST /payment-providers/{providerId}/status - Report integration status (reportPaymentProviderStatus)
- GET /payment-providers/{providerId}/balances - Read a payment provider's balances: clearing and the last payout (getPaymentProviderBalances)
### Payment methods
- GET /payment-methods - List the payment method catalogue (listPaymentMethodCatalogue)
- GET /payment-methods/policies/{scopeType}/{scopeId} - Get the payment methods one level offers (getPaymentMethodPolicy)
- PUT /payment-methods/policies/{scopeType}/{scopeId} - Set the payment methods one level offers (putPaymentMethodPolicy)
### Cash sessions
- GET /cash-sessions/settings - Get the organisation's cash session settings (getCashSessionSettings)
- PUT /cash-sessions/settings - Set the organisation's cash session settings (putCashSessionSettings)
- POST /cash-sessions - Open a cash session (openCashSession)
- GET /cash-sessions - List cash sessions (listCashSessions)
- GET /cash-sessions/{sessionId} - Get a cash session (getCashSession)
- GET /cash-sessions/{sessionId}/report - Get a cash session's X or Z report (getCashSessionReport)
- POST /cash-sessions/{sessionId}/movements - Pay in, pay out or drop to the safe (recordCashMovement)
- POST /cash-sessions/{sessionId}/handover - Hand the drawer to the next person (handOverCashSession)
- POST /cash-sessions/{sessionId}/close - Close a cash session with a blind count (closeCashSession)
- POST /cash-sessions/{sessionId}/approve - Approve a cash session's variance (approveCashSession)
### Delivery methods
- GET /delivery-methods - List the delivery method catalogue (listDeliveryMethodCatalogue)
- GET /delivery-methods/policies/{scopeType}/{scopeId} - Get the delivery methods one level offers (getDeliveryMethodPolicy)
- PUT /delivery-methods/policies/{scopeType}/{scopeId} - Set the delivery methods one level offers (putDeliveryMethodPolicy)
### Fulfilment
- GET /fulfilment/providers - List the fulfilment providers (listFulfilmentProviders)
- GET /fulfilment/units - List fulfilment units (a queue) (listFulfilmentUnits)
- GET /fulfilment/units/{unitId} - Get a fulfilment unit (getFulfilmentUnit)
- POST /fulfilment/units/{unitId}/holds - Hold a fulfilment unit (holdFulfilmentUnit)
- POST /fulfilment/units/{unitId}/holds/{holdId}/release - Release a hold (releaseFulfilmentHold)
- POST /fulfilment/units/{unitId}/retry - Retry a failed unit (retryFulfilmentUnit)
- POST /fulfilment/units/{unitId}/cancel - Cancel a fulfilment unit (cancelFulfilmentUnit)
- POST /fulfilment/units/{unitId}/progress - Report progress on a unit (reportFulfilmentProgress)
- POST /fulfilment/units/{unitId}/collect - Hand a unit over at the collection desk (collectFulfilmentUnit)
- GET /fulfilment/units/{unitId}/collection - Who collected a unit (getFulfilmentCollection)
- POST /fulfilment/units/{unitId}/print - Print a unit's tickets (printFulfilmentUnit)
### Shipments
- GET /fulfilment/settings - Get the organisation's fulfilment settings (getFulfilmentSettings)
- PUT /fulfilment/settings - Set the organisation's fulfilment settings (putFulfilmentSettings)
- POST /fulfilment/units/{unitId}/shipments - Open a parcel for a post unit (createShipment)
- GET /fulfilment/shipments - List parcels (listShipments)
- GET /fulfilment/shipments/{shipmentId} - Get a parcel (getShipment)
- POST /fulfilment/shipments/{shipmentId}/scan - Scan a ticket into a parcel (scanIntoShipment)
- POST /fulfilment/shipments/{shipmentId}/items - Put items in a parcel without scanning (addShipmentItems)
- POST /fulfilment/shipments/{shipmentId}/dispatch - Dispatch a parcel (dispatchShipment)
- POST /fulfilment/shipments/{shipmentId}/delivered - Mark a parcel delivered (markShipmentDelivered)
- POST /fulfilment/shipments/{shipmentId}/returned - Mark a parcel returned (markShipmentReturned)
- POST /fulfilment/shipments/{shipmentId}/cancel - Cancel a parcel being packed (cancelShipment)
- POST /fulfilment/shipments/{shipmentId}/encode - Encode a wristband for a holder and pack it (encodeIntoShipment)
### Media types
- GET /fulfilment/media-types - List media types (listMediaTypes)
- POST /fulfilment/media-types - Add a media type (createMediaType)
- PATCH /fulfilment/media-types/{mediaTypeId} - Change a media type (updateMediaType)
### Printer media
- GET /fulfilment/printers/{deviceId}/media - What a printer has loaded (getPrinterMedia)
- PUT /fulfilment/printers/{deviceId}/media - Set what a printer has loaded (setPrinterMedia)
### Media stock
- GET /fulfilment/stock - Media stock (listMediaStock)
- POST /fulfilment/stock/movements - Move media stock (moveMediaStock)
- GET /fulfilment/stock/{stockId}/movements - Media stock movements (listMediaStockMovements)
### Exchange
- POST /fulfilment/exchange/lookup - Look up what is being exchanged (lookupExchange)
- POST /fulfilment/exchange/holders/{slotId} - Swap one holder for a wristband (exchangeHolder)
- GET /fulfilment/exchanges - Exchanges made (listExchanges)
### Fulfilment Exceptions
- GET /fulfilment/exceptions - List fulfilment exceptions (listFulfilmentExceptions)
- POST /fulfilment/exceptions/{taskId}/resolve - Resolve a fulfilment exception (resolveFulfilmentException)
### Stored value
- GET /stored-value/accounts - List stored-value accounts (listStoredValueAccounts)
- GET /stored-value/accounts/{storedValueAccountId} - Get a stored-value account (getStoredValueAccount)
- GET /stored-value/accounts/{storedValueAccountId}/activity - Get a stored-value account's activity (getStoredValueActivity)
- POST /stored-value/accounts/{storedValueAccountId}/adjustments - Adjust a stored-value balance (adjustStoredValue)
- POST /stored-value/accounts/{storedValueAccountId}/status - Freeze, unfreeze or void a stored-value account (setStoredValueStatus)
- POST /stored-value/credits - Give a customer account credit (giveStoredValueCredit)
- GET /stored-value/settings - Get the organisation's stored-value settings (getStoredValueSettings)
- PUT /stored-value/settings - Set the organisation's stored-value settings (updateStoredValueSettings)
- GET /stored-value/reports/closed-loop - Get the closed-loop value report (getStoredValueClosedLoopReport)
### Gift cards
- GET /stored-value/gift-card-products - List gift card products (listGiftCardProducts)
- POST /stored-value/gift-card-products - Create a gift card product (createGiftCardProduct)
- PATCH /stored-value/gift-card-products/{productId} - Update a gift card product (updateGiftCardProduct)
- POST /stored-value/gift-cards/lookup - Look up a gift card by its code (lookUpGiftCard)
- POST /stored-value/gift-cards/{storedValueAccountId}/issue-code - Issue a gift card's code (issueGiftCardCode)
### Sales Channels
- POST /sales-channels - Create sales channel (createSalesChannel)
- GET /sales-channels - List sales channels (listSalesChannels)
- GET /sales-channels/{channelId} - Get sales channel (getSalesChannel)
- PATCH /sales-channels/{channelId} - Update sales channel (updateSalesChannel)
- DELETE /sales-channels/{channelId} - Delete sales channel (deleteSalesChannel)
- GET /my/sales-channels - The channels I may act through (getMySalesChannels)
### Keys
- POST /sales-channels/{channelId}/keys - Issue channel key (issueSalesChannelKey)
- GET /sales-channels/{channelId}/keys - List channel keys (listSalesChannelKeys)
- POST /sales-channels/{channelId}/keys/{keyId}/revoke - Revoke channel key (revokeSalesChannelKey)
### Listings
- POST /sales-channels/{channelId}/listings - List event on channel (createSalesChannelListing)
- GET /sales-channels/{channelId}/listings - List channel listings (listSalesChannelListings)
- PATCH /sales-channels/{channelId}/listings/{listingId} - Update channel listing (updateSalesChannelListing)
- DELETE /sales-channels/{channelId}/listings/{listingId} - Remove channel listing (deleteSalesChannelListing)
### Channel Access
- GET /sales-channels/{channelId}/access - List who has access to the channel (listSalesChannelAccess)
- POST /sales-channels/{channelId}/access - Grant access to the channel (grantSalesChannelAccess)
- DELETE /sales-channels/{channelId}/access/{accessId} - Revoke access to the channel (revokeSalesChannelAccess)
### Carts
- POST /cart - Create cart (createStaffCart)
- GET /cart/{cartId} - Get cart (getStaffCart)
- POST /cart/{cartId}/items - Add cart item (addStaffCartItem)
- POST /cart/{cartId}/seats - Reserve seats (reserveStaffCartSeats)
- PATCH /cart/{cartId}/seats/{occurrenceId}/{seat} - Change a seat's ticket type (changeStaffCartSeat)
- DELETE /cart/{cartId}/seats/{occurrenceId}/{seat} - Give a seat back (releaseStaffCartSeat)
- DELETE /cart/{cartId}/items/{itemId} - Remove cart item (removeStaffCartItem)
- GET /cart/{cartId}/options - Delivery and payment options (getStaffCartOptions)
- GET /cart/{cartId}/quote - Quote the cart (getStaffCartQuote)
- PUT /cart/{cartId}/choices - Choose delivery and payment (setStaffCartChoices)
- GET /cart/{cartId}/holders - Holders and add-ons on a cart (getStaffCartHolders)
- PUT /cart/{cartId}/holders - Name holders and place add-ons on a cart (setStaffCartHolders)
- POST /cart/{cartId}/checkout - Checkout cart (checkoutStaffCart)
- GET /cart/{cartId}/checkout - Get cart checkout (getStaffCartCheckout)
- POST /cart/{cartId}/unlocks - Unlock a price for this sale (addStaffCartUnlock)
- DELETE /cart/{cartId}/unlocks/{unlockId} - Remove an unlock for this sale (removeStaffCartUnlock)
- POST /cart/{cartId}/codes - Apply a code to the cart (applyStaffCartCode)
- DELETE /cart/{cartId}/codes/{codeId} - Remove a code from the cart (removeStaffCartCode)
### Fee Profiles
- POST /fee-profiles - Create fee profile (createFeeProfile)
- GET /fee-profiles - List fee profiles (listFeeProfiles)
- GET /fee-profiles/{profileId} - Get fee profile (getFeeProfile)
- PATCH /fee-profiles/{profileId} - Update fee profile (updateFeeProfile)
- DELETE /fee-profiles/{profileId} - Delete fee profile (deleteFeeProfile)
### Fee Settings
- GET /fee-settings/surcharge-rules - List the surcharge rules by jurisdiction (listSurchargeRules)
- GET /fee-settings/{scopeType}/{scopeId} - Get fee settings (getFeeSettings)
- PUT /fee-settings/{scopeType}/{scopeId} - Set fee settings (putFeeSettings)
### Concierge
- GET /sales-channels/{channelId}/concierge - Get a channel's Concierge settings (getSalesChannelConcierge)
- PUT /sales-channels/{channelId}/concierge - Replace a channel's Concierge settings (replaceSalesChannelConcierge)
- GET /sales-channels/{channelId}/concierge/resolved - Get a channel's resolved Concierge (getResolvedSalesChannelConcierge)
### AI Action Limits
- GET /ai-action-limits - List AI action limits (listAiActionLimits)
- PUT /ai-action-limits - Set AI action limits (setAiActionLimits)
- GET /ai-action-limits/resolved - Resolve the AI action limits for an account (resolveAiActionLimits)
### Concierge Simulations
- POST /sales-channels/{channelId}/concierge/simulations - Start a Concierge simulation for a channel (startConciergeSimulation)
- GET /sales-channels/{channelId}/concierge/simulations - List a channel's Concierge simulations (listConciergeSimulations)
- GET /concierge/simulations/{runId} - Get a Concierge simulation (getConciergeSimulation)
- POST /concierge/simulations/regression - Add a Concierge simulation regression scenario (addConciergeSimulationRegression)
### Brands
- GET /brands - List brands (listBrands)
- POST /brands - Create brand (createBrand)
- GET /brands/{id} - Get brand (getBrand)
- PATCH /brands/{id} - Update brand (updateBrand)
- DELETE /brands/{id} - Delete brand (deleteBrand)
- GET /brands/{id}/resolved - Get resolved brand (getResolvedBrand)
### Tags
- GET /tag-definitions - List tag definitions (listTagDefinitions)
- POST /tag-definitions - Define a tag (createTagDefinition)
- GET /tag-definitions/{id} - Get a tag definition (getTagDefinition)
- PATCH /tag-definitions/{id} - Update a tag definition (updateTagDefinition)
- DELETE /tag-definitions/{id} - Archive a tag definition (archiveTagDefinition)
### Knowledge
- GET /knowledge/articles - List knowledge base articles (listKnowledgeArticles)
- POST /knowledge/articles - Write a knowledge base article (createKnowledgeArticle)
- GET /knowledge/articles/{articleId} - Get a knowledge base article (getKnowledgeArticle)
- PATCH /knowledge/articles/{articleId} - Edit a knowledge base article (updateKnowledgeArticle)
- DELETE /knowledge/articles/{articleId} - Archive a knowledge base article (archiveKnowledgeArticle)
- GET /knowledge/articles/{articleId}/preview - Preview a knowledge base article in a context (previewKnowledgeArticle)
- GET /knowledge/fact-keys - List the fact keys (listKnowledgeFactKeys)
- GET /knowledge/facts - List stored facts (listKnowledgeFacts)
- POST /knowledge/facts - Store a fact (createKnowledgeFact)
- GET /knowledge/facts/{factId} - Get a stored fact (getKnowledgeFact)
- PATCH /knowledge/facts/{factId} - Change a stored fact (updateKnowledgeFact)
- DELETE /knowledge/facts/{factId} - Delete a stored fact (deleteKnowledgeFact)
- GET /knowledge/resolved - Resolve the knowledge base for a context (getResolvedKnowledge)
- GET /knowledge/search - Search the knowledge base in a context (searchKnowledgeArticles)
- GET /knowledge/policies/refund - Generated refund and exchange policy text (getKnowledgeRefundPolicyText)
- GET /knowledge/gaps - Questions the Concierge AI could not answer (listKnowledgeGaps)
### Codes
- GET /codes - List codes (listCodes)
- POST /codes - Create a code (createCode)
- GET /codes/{codeId} - Get a code (getCode)
- PATCH /codes/{codeId} - Update a code (updateCode)
- DELETE /codes/{codeId} - Archive a code (archiveCode)
### Entitlements
- POST /passes/{barcode}/redeem - Redeem a pass (redeemPass)
### Pass Identifiers
- GET /passes/{passId}/identifiers - List a pass's identifiers (listPassIdentifiers)
- POST /passes/{passId}/identifiers - Bind an identifier to a pass (bindPassIdentifier)
- POST /passes/{passId}/identifiers/{identifierId}/revoke - Revoke one of a pass's identifiers (revokePassIdentifier)
- POST /passes/{passId}/reissue - Re-issue a pass (reissuePass)
- POST /passes/{passId}/identifiers/signed - Give a pass a signed barcode (signPass)
### Pass Holders
- POST /passes/{passId}/name - Name a pass's holder, or change the name (changePassHolderName)
- POST /passes/{passId}/transfer - Transfer a pass to another person (transferPass)
- GET /passes/{passId}/holder-changes - A pass's holder history (listPassHolderChanges)
### Pass Wallet
- GET /passes/{passId}/wallet/apple - Get a pass as an Apple Wallet pass (getPassAppleWallet)
- GET /passes/{passId}/wallet/google - Get a pass as a Google Wallet pass (getPassGoogleWallet)
### Long-lived Passes
- POST /passes/long-lived - Create a long-lived pass for a customer (createLongLivedPass)
- GET /passes/long-lived - List a customer's long-lived passes (listLongLivedPasses)
### Pass Contents
- GET /passes/{passId}/entitlements - What a pass carries (listPassEntitlements)
- POST /passes/{passId}/entitlements - Move an entitlement onto this pass (loadEntitlementOntoPass)
- GET /passes/{passId}/moves - Entitlements moved onto or off a pass (listPassMoves)
### Pass Policies
- GET /pass-policies/{scopeType}/{scopeId} - Get an event's or a ticket type's pass policy (getPassPolicy)
- PUT /pass-policies/{scopeType}/{scopeId} - Set an event's or a ticket type's pass policy (putPassPolicy)
### Ticket Templates
- GET /ticket-templates - List ticket templates (listTicketTemplates)
- POST /ticket-templates - Create ticket template (createTicketTemplate)
- GET /ticket-templates/{id} - Get ticket template (getTicketTemplate)
- PATCH /ticket-templates/{id} - Update ticket template (updateTicketTemplate)
- DELETE /ticket-templates/{id} - Delete ticket template (deleteTicketTemplate)
### Scan Configs
- POST /scan-configs - Create a scan configuration (createScanConfig)
- GET /scan-configs - List scan configurations (listScanConfigs)
- GET /scan-configs/{configId} - Get a scan configuration (getScanConfig)
- PATCH /scan-configs/{configId} - Update a scan configuration (updateScanConfig)
- DELETE /scan-configs/{configId} - Delete a scan configuration (deleteScanConfig)
- PUT /scan-configs/{configId}/devices - Assign scanner devices to a configuration (setScanConfigDevices)
### Scan
- GET /scan/config - Get the device scan scope (getScanConfigForDevice)
- GET /scan/manifest - Get the offline validation dataset (getScanManifest)
- POST /scan/operations - Submit scan operations (submitScanOperations)
- GET /scan/keys - The organisation's pass signing keys (getScanSigningKeys)
### Scans
- GET /scans - List scans (staff reporting) (listScans)
- POST /redemptions/{redemptionId}/reverse - Reverse a redemption (reverseRedemption)
### Assets
- GET /assets - List assets (listAssets)
- POST /assets - Upload an asset (uploadAsset)
- POST /assets/uploads - Start a signed upload (createAssetUpload)
- POST /assets/{id}/complete - Complete a signed upload (completeAssetUpload)
- GET /assets/{id} - Get an asset (getAsset)
- DELETE /assets/{id} - Delete an asset (deleteAsset)
- GET /assets/{id}/content - Get an asset's bytes (getAssetContent)
### Device Pairing
- POST /devices/pairing - Start device pairing (startDevicePairing)
- GET /devices/pairing/{pairingId} - Poll device pairing status (getDevicePairing)
- POST /devices/pairing/claim - Claim a device pairing (claimDevicePairing)
- POST /devices/pairing/{pairingId}/confirm - Confirm device adoption (confirmDevicePairing)
- POST /devices/pairing/{pairingId}/reject - Reject device adoption (rejectDevicePairing)
### Device Auth
- POST /devices/token - Mint a device token (issueDeviceToken)
- GET /devices/jwks - Device JWKS (getDeviceJwks)
### Device Self
- GET /devices/me - Get own device record (getDeviceMe)
- POST /devices/me/heartbeat - Send a device heartbeat (deviceHeartbeat)
- PUT /devices/me/peripherals - Declare peripherals (declareDevicePeripherals)
- GET /devices/me/commands - Long-poll for commands (fetchDeviceCommands)
- POST /devices/me/commands/{commandId}/progress - Report command progress (reportCommandProgress)
- POST /devices/me/commands/{commandId}/result - Report command result (reportCommandResult)
- POST /devices/me/events - Upload device events (ingestDeviceEvents)
- GET /devices/me/certificate - Get local TLS certificate (getDeviceCertificate)
### Devices
- GET /devices/printers - List printers (listPrinters)
- GET /devices/nfc-readers - List NFC readers (listNfcReaders)
- GET /devices/media-catalog - Print capability & standard-media catalog (getDeviceMediaCatalog)
- GET /devices/commands/{commandId} - Get a command (getDeviceCommand)
- GET /devices - List devices (listDevices)
- GET /devices/{deviceId} - Get device (getDevice)
- PATCH /devices/{deviceId} - Update device (updateDevice)
- DELETE /devices/{deviceId} - Delete device (deleteDevice)
- POST /devices/{deviceId}/revoke - Revoke device (revokeDevice)
- PUT /devices/{deviceId}/roles - Assign device roles (assignDeviceRoles)
- POST /devices/{deviceId}/credentials/rotate - Rotate device credential (rotateDeviceCredential)
- POST /devices/{deviceId}/commands - Dispatch a command (dispatchDeviceCommand)
- GET /devices/{deviceId}/commands - List device commands (listDeviceCommands)
- GET /devices/{deviceId}/events - List device events (listDeviceEvents)
- POST /devices/{deviceId}/local-tokens - Mint a Local Command Token (mintLocalToken)
### Locations
- POST /locations - Create location (createLocation)
- GET /locations - List locations (listLocations)
- GET /locations/{locationId} - Get location (getLocation)
- PATCH /locations/{locationId} - Update location (updateLocation)
- DELETE /locations/{locationId} - Delete location (deleteLocation)
### Reports
- GET /reports/summary - Sales summary KPIs (getReportsSummary)
- GET /reports/timeseries - Sales over time (getReportsTimeseries)
- GET /reports/inventory - Inventory sell-through (getReportsInventory)
- GET /reports/meta - Semantic model (getReportsMeta)
- POST /reports/query - Run an ad-hoc query (runReportsQuery)
- GET /reports/saved - List saved reports (listSavedReports)
- POST /reports/saved - Create a saved report (createSavedReport)
- GET /reports/saved/{id} - Get a saved report (getSavedReport)
- PATCH /reports/saved/{id} - Update a saved report (updateSavedReport)
- DELETE /reports/saved/{id} - Delete a saved report (deleteSavedReport)
- POST /reports/saved/{id}/run - Run a saved report (runSavedReport)
- GET /reports/dashboards - List dashboards (listDashboards)
- POST /reports/dashboards - Create a dashboard (createDashboard)
- GET /reports/dashboards/{id} - Get a dashboard (getDashboard)
- PATCH /reports/dashboards/{id} - Update a dashboard (updateDashboard)
- DELETE /reports/dashboards/{id} - Delete a dashboard (deleteDashboard)
- GET /reports/templates - List standard templates (listReportTemplates)
- POST /reports/templates/{key}/install - Install a template (installReportTemplate)
### Webhook Endpoints
- POST /webhook-endpoints - Create a webhook endpoint (createWebhookEndpoint)
- GET /webhook-endpoints - List webhook endpoints (listWebhookEndpoints)
- GET /webhook-endpoints/{endpointId} - Get a webhook endpoint (getWebhookEndpoint)
- PATCH /webhook-endpoints/{endpointId} - Update a webhook endpoint (updateWebhookEndpoint)
- DELETE /webhook-endpoints/{endpointId} - Delete a webhook endpoint (deleteWebhookEndpoint)
- POST /webhook-endpoints/{endpointId}/test - Send a test delivery (testWebhookEndpoint)
### Webhook Deliveries
- GET /webhook-deliveries - List webhook deliveries (listWebhookDeliveries)
- GET /webhook-deliveries/{deliveryId} - Get a webhook delivery (getWebhookDelivery)
- GET /webhook-inbound - List recent inbound webhook events (ingress log) (listInboundWebhooks)
### Webhook Event Types
- GET /webhook-event-types - List subscribable webhook event types (listWebhookEventTypes)
### Integrations
- GET /integrations - List the integration marketplace (listIntegrations)
- GET /integrations/connected-apps - List connected OAuth apps for the org (listConnectedApps)
- GET /integrations/scopes - What each OAuth scope means (listIntegrationScopes)
- GET /integrations/grants - List members' grants to connected apps (listOrganisationGrants)
- DELETE /integrations/grants/{grantId} - Revoke a member's grant to a connected app (revokeOrganisationGrant)
- GET /integrations/{id} - Get an integration (getIntegration)
- DELETE /integrations/{id}/connection - Revoke a connected app (revokeIntegrationConnection)
### Client Versions
- GET /client-versions - List client versions in use (listClientVersions)
### Ledger
- GET /ledger/balances - Read an account ledger balance (getLedgerBalances)
- GET /ledger/statement - Read a party's statement over a date range (getLedgerStatement)
- POST /ledger/settlements - Record a settlement between two accounts (createLedgerSettlement)
- GET /ledger/settlements - List the settlements recorded between parties (listLedgerSettlements)
- GET /ledger/settlements/{settlementId} - Read a settlement's statement (getLedgerSettlement)
- POST /ledger/settlements/{settlementId}/reversal - Reverse a settlement recorded in error (reverseLedgerSettlement)
- GET /ledger/events/{eventId}/statement - Read an event's statement: owed, settled and outstanding (getLedgerEventStatement)
- GET /ledger/exceptions - List the money events the ledger is missing (listLedgerExceptions)
- POST /ledger/exceptions/{exceptionId}/retry - Retry a posting exception's posting (retryLedgerException)
- GET /ledger/payouts - List the payouts the payment providers made (listLedgerPayouts)
- GET /ledger/payouts/{payoutId} - Read a payout's statement (getLedgerPayout)
### Cases
- POST /cases - Open a case (createCase)
- GET /cases - List cases (listCases)
- GET /cases/{caseId} - Get a case (getCase)
- PATCH /cases/{caseId} - Update a case (updateCase)
- POST /cases/{caseId}/links - Link a case (linkCase)
- DELETE /cases/{caseId}/links/{linkId} - Unlink a case (unlinkCase)
- GET /cases/{caseId}/activities - List a case's activities (listCaseActivities)
- POST /cases/{caseId}/activities - Log an activity on a case (logCaseActivity)
- GET /cases/{caseId}/actions - List the actions you may take on a case (listCaseActions)
- POST /cases/{caseId}/actions/{action} - Take an action on a case (takeCaseAction)
- GET /cases/{caseId}/proposals - List the AI's proposals on a case (listCaseProposals)
- POST /cases/{caseId}/macros/{macroId}/render - Render a macro for a case (renderCaseMacro)
- PUT /cases/{caseId}/presence - Say you are viewing or replying to a case (putCasePresence)
- DELETE /cases/{caseId}/presence - Leave a case (deleteCasePresence)
### Support
- GET /support/inbox - Get the support inbox (getSupportInbox)
- GET /support/settings - Get the support settings (getSupportSettings)
- PUT /support/settings - Replace the support settings (putSupportSettings)
- GET /support/macros - List macros (listSupportMacros)
- POST /support/macros - Create a macro (createSupportMacro)
- GET /support/macros/{macroId} - Get a macro (getSupportMacro)
- PATCH /support/macros/{macroId} - Update a macro (updateSupportMacro)
- DELETE /support/macros/{macroId} - Delete a macro (deleteSupportMacro)
- GET /support/email/addresses - List inbound email addresses (listInboundAddresses)
- POST /support/email/addresses - Add an inbound email address (createInboundAddress)
- GET /support/email/forwarding-confirmations - List forwarding confirmations (listForwardingConfirmations)
### Tasks
- POST /tasks - Raise a task (createTask)
- GET /tasks - List tasks (listTasks)
- GET /tasks/counts - Count the inbox's tasks (getTaskCounts)
- GET /tasks/{taskId} - Get a task (getTask)
- POST /tasks/{taskId}/claim - Claim a task (claimTask)
- POST /tasks/{taskId}/release - Release a task (releaseTask)
- POST /tasks/{taskId}/reassign - Reassign a task (reassignTask)
- POST /tasks/{taskId}/decision - Decide a task (decideTask)
### Task Types
- GET /task-types - List the task types (listTaskTypes)
### Task Queues
- POST /task-queues - Create a task queue (createTaskQueue)
- GET /task-queues - List task queues (listTaskQueues)
- GET /task-queues/{queueId} - Get a task queue (getTaskQueue)
- PATCH /task-queues/{queueId} - Update a task queue (updateTaskQueue)
### Object types
- GET /object-types - List object types (listObjectTypes)
- GET /object-types/{type} - Get an object type (getObjectType)
### Flows
- GET /flows - List flows (listFlows)
- GET /flows/{key} - Get a flow (getFlow)
### Tax Registrations
- GET /tax/registrations - List tax registrations (listTaxRegistrations)
- POST /tax/registrations - Add a tax registration (createTaxRegistration)
- PATCH /tax/registrations/{registrationId} - Correct or end a tax registration (updateTaxRegistration)
- DELETE /tax/registrations/{registrationId} - Remove a tax registration made in error (deleteTaxRegistration)
### Tax Settings
- GET /tax/settings/{partyId} - Get a party's tax settings (getPartyTaxSettings)
- PUT /tax/settings/{partyId} - Set a party's tax settings (putPartyTaxSettings)
- GET /tax/rates/gb - The UK VAT rate table (listUkVatRates)
### VAT Returns
- GET /tax/returns/{partyId} - VAT return figures for a period (getVatReturnFigures)
### Tax Documents
- GET /orders/{orderId}/tax-documents - List an order's tax documents (listOrderTaxDocuments)
- POST /orders/{orderId}/tax-documents - Issue tax documents for an order (issueOrderTaxDocuments)
- GET /orders/{orderId}/tax-documents/{documentId} - Get a tax document (getOrderTaxDocument)
- GET /orders/{orderId}/tax-documents/{documentId}/document.html - A tax document as a printable page (renderOrderTaxDocument)
## Live API {#ops-live-api}
Base URL: https://live.ticketlayer.com (staging: https://live.staging.t9r.dev). OpenAPI document: https://docs.ticketlayer.com/openapi/live.json. Reference pages: https://docs.ticketlayer.com/api/live/.
### Session
- POST /session - Bootstrap a Live session (createLiveSession)
- GET /session - Get the current session (getLiveSession)
- DELETE /session - Destroy the current session (deleteLiveSession)
- POST /session/order-access - Attach an order access token to the session (attachLiveOrderAccess)
### Queue
- GET /queue/status - Get waiting-room status (getLiveQueueStatus)
- POST /queue/join - Join the waiting room (joinLiveQueue)
### Seat map
- POST /seatmap/tokens - Mint a seat map token (createSeatmapToken)
### Concierge
- GET /v1/concierge/config - Get the channel's Concierge configuration (getConciergeConfig)
- GET /v1/concierge/search - Search the knowledge base (searchConcierge)
- POST /v1/concierge/contact - Send the contact form (contactConcierge)
- POST /v1/concierge/verify/start - Send a verification code (startConciergeVerification)
- POST /v1/concierge/verify/complete - Verify with the code (completeConciergeVerification)
- POST /v1/concierge/turns - Take an AI turn (createConciergeTurn)
- GET /v1/concierge/articles - List the channel's help articles (listConciergeArticles)
- GET /v1/concierge/articles/{articleId} - Get a help article (getConciergeArticle)
- GET /v1/concierge/policies/refund - Get the refund and exchange policy text (getConciergeRefundPolicy)
- POST /v1/concierge/proposals/{proposalId}/confirm - Confirm a proposal (confirmConciergeProposal)
- POST /v1/concierge/conversations/{conversationId}/close - Close the conversation (closeConciergeConversation)
### Sales
- GET /sales/channel - Resolve a channel by domain or publishable key (resolveSalesChannel)
- GET /sales/events/{eventId} - Get event catalogue detail (getSalesEvent)
- GET /sales/occurrences/{occurrenceId}/seatmap - Get an occurrence's seat map pointer (getSalesOccurrenceSeatmap)
- POST /sales/occurrences/{occurrenceId}/best-available - Find the best available seats (getSalesOccurrenceBestAvailable)
- GET /sales/theme - Get channel theme (getSalesTheme)
- GET /sales/concierge/config - Get the channel's Concierge config (getSalesConciergeConfig)
### Sales Customer Auth
- POST /sales/customer-auth/magic-link - Request magic link (requestSalesMagicLink)
### Sales Customer Tokens
- POST /sales/customer-tokens - Exchange identity proof for customer token (createSalesCustomerToken)
- POST /sales/customer-tokens/refresh - Refresh a customer token (refreshSalesCustomerToken)
### Sales Listings
- GET /sales/listings - List active listings (listSalesListings)
### Sales Carts
- POST /sales/carts - Create cart (createSalesCart)
- GET /sales/carts/{cartId} - Get cart (getSalesCart)
- DELETE /sales/carts/{cartId} - Abandon cart (abandonSalesCart)
- POST /sales/carts/{cartId}/items - Add cart item (addSalesCartItem)
- POST /sales/carts/{cartId}/customer - Attach the signed-in customer to a cart (attachSalesCartCustomer)
- DELETE /sales/carts/{cartId}/items/{itemId} - Remove cart item (removeSalesCartItem)
- POST /sales/carts/{cartId}/seats - Reserve seats (reserveSalesCartSeats)
- PATCH /sales/carts/{cartId}/seats/{occurrenceId}/{seat} - Change a seat's ticket type (changeSalesCartSeat)
- DELETE /sales/carts/{cartId}/seats/{occurrenceId}/{seat} - Give a seat back (releaseSalesCartSeat)
- POST /sales/carts/{cartId}/codes - Apply a code to the cart (applySalesCartCode)
- DELETE /sales/carts/{cartId}/codes/{codeId} - Remove a code from the cart (removeSalesCartCode)
- GET /sales/carts/{cartId}/options - Delivery and payment options (getSalesCartOptions)
- GET /sales/carts/{cartId}/quote - Quote the cart (getSalesCartQuote)
- PUT /sales/carts/{cartId}/choices - Choose delivery and payment (setSalesCartChoices)
- GET /sales/carts/{cartId}/holders - Holders and add-ons (getSalesCartHolders)
- PUT /sales/carts/{cartId}/holders - Name holders and place add-ons (setSalesCartHolders)
- POST /sales/carts/{cartId}/checkout - Checkout cart (checkoutSalesCart)
- GET /sales/carts/{cartId}/checkout - Get cart checkout (getSalesCartCheckout)
- POST /sales/carts/{cartId}/gift-cards - Add a gift card to the cart (addSalesCartGiftCard)
- DELETE /sales/carts/{cartId}/gift-cards/{giftCardId} - Remove a gift card from the cart (removeSalesCartGiftCard)
### Sales My Orders
- GET /sales/my-orders - List my orders (listMySalesOrders)
- GET /sales/my-credit - Get my account credit (getMySalesCredit)
- GET /sales/my-credit/{storedValueAccountId}/activity - Get my account credit activity (getMySalesCreditActivity)
### Sales Gift Cards
- GET /sales/gift-card-products - List gift cards on sale (listSalesGiftCardProducts)
- POST /sales/gift-cards/balance - Check a gift card's balance (getSalesGiftCardBalance)
### Sales Order Payments
- POST /sales/orders/{orderId}/stored-value-payments - Pay with a gift card or account credit (spendSalesOrderStoredValue)
- POST /sales/orders/{orderId}/payments - Create order payment (createSalesOrderPayment)
- POST /sales/orders/{orderId}/payments/paypal - Start a PayPal payment (createSalesOrderPaypalPayment)
- POST /sales/orders/{orderId}/payments/{paymentId}/paypal/capture - Capture an approved PayPal payment (captureSalesOrderPaypalPayment)
- POST /sales/orders/{orderId}/payments/{paymentId}/sandbox/confirm - Confirm a sandbox payment (confirmSalesOrderSandboxPayment)
- POST /sales/orders/{orderId}/payments/{paymentId}/adyen/session-result - Read an Adyen Drop-in result (readSalesOrderAdyenSessionResult)
### Sales Orders
- GET /sales/orders/{orderId} - Get order (getSalesOrder)
- GET /sales/orders/{orderId}/holders - Holders and add-ons (Manage my booking) (getSalesOrderHolders)
- PUT /sales/orders/{orderId}/holders/{slotId} - Name a holder (Manage my booking) (nameSalesOrderHolder)
- GET /sales/orders/{orderId}/holders/{slotId} - One holder (their own link, or the booker) (getSalesOrderHolder)
- POST /sales/orders/{orderId}/holders/{slotId}/link - Give a named guest their own link (createSalesOrderHolderLink)
- POST /sales/orders/{orderId}/add-ons/assign - Put an add-on on a holder (Manage my booking) (assignSalesOrderAddOn)
- POST /sales/orders/{orderId}/holders/{slotId}/activate - Activate a posted wristband (Manage my booking) (activateSalesHolderWristband)
- GET /sales/orders/{orderId}/passes/{passId}/ticket.pdf - Download a ticket PDF (getSalesOrderTicketPdf)
### Sales Passes
- GET /sales/passes/{passId}/rotating-seed - Get a pass's rotating-code seed (getSalesPassRotatingSeed)
- GET /sales/passes/{passId}/wallet/apple - Add a pass to Apple Wallet (getSalesPassAppleWallet)
- GET /sales/passes/{passId}/wallet/google - Add a pass to Google Wallet (getSalesPassGoogleWallet)
### Sales Refund Requests
- POST /sales/orders/{orderId}/refund-requests - Request refund or exchange (createSalesOrderRefundRequest)
### Sales Customers
- POST /sales/customers - Create customer (createSalesCustomer)
- GET /sales/customers/{customerId} - Get customer (getSalesCustomer)
- PATCH /sales/customers/{customerId} - Update customer (updateSalesCustomer)
### Sales Knowledge
- GET /sales/knowledge/articles - List the channel's help articles (listSalesKnowledgeArticles)
- GET /sales/knowledge/search - Search the channel's help articles (searchSalesKnowledge)
- GET /sales/knowledge/articles/{articleId} - Get a help article (getSalesKnowledgeArticle)
- GET /sales/knowledge/facts - Get the resolved facts (getSalesKnowledgeFacts)
- GET /sales/knowledge/policies/refund - Get the refund and exchange policy text (getSalesRefundPolicyText)