Create and publish charts inside your product
Your backend checks workspace and chart ownership, then mints a short-lived dse_… session for an exact origin. EmbeddedDesigner handles iframe lifecycle, validated messages, relaunch and teardown.
Embed a seat picker in your web or mobile app and keep your own checkout. SeatLayer runs the seat map, seat holds, live availability and booking, from a 300-seat theatre to a 200,000-seat stadium. Start with the interactive seat map guide or the quickstart.
SeatLayer builds this seat map engine and sells tickets on it too. Already selling on Hosted Ticketing? Keep the same account and charts and add the SDK for new events. How it compares
200,000-seat stadium · 100,000-seat arena · Renderer measurements
$ npm install @seatlayer/js
$ npm install @seatlayer/react
Tested on public venue files with simulated buyers, September 2026. How we measured loading · How we measured buyers · Every run on GitHub
onCheckout hands your server a holdId. The seats stay held while the buyer pays.bookingRef. A signed webhook confirms the result.Private audiences replace the public key with a buyer-access provider on your server.
import { SeatPicker } from '@seatlayer/js'; const picker = new SeatPicker({ container: '#seat-picker', event: '<YOUR_EVENT_KEY>', // change this at runtime publicKey: '<YOUR_PUBLIC_KEY>', // publishable; public sale only onCheckout: async (_, __, handoff) => { await fetch('/api/checkout', { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ holdId: handoff.holdId }), }); }, }); picker.render();
// your server: trust fresh hold data, never browser prices const base = `https://api.seatlayer.io/v1/events/${eventKey}`; const headers = { Authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}` }; const hold = await fetch(`${base}/holds/${holdId}`, { headers }) .then(r => r.json()); if (hold.status !== 'active') return conflict(); const order = await createOrderFrom(hold.items); await fetch(`${base}/book`, { method: 'POST', headers: { ...headers, 'content-type': 'application/json' }, body: JSON.stringify({ holdId, bookingRef: order.id }), });
// capture raw bytes before JSON parsing const expected = crypto.createHmac('sha256', process.env.WHSEC) .update(req.body).digest(); const sig = req.headers['x-seatlayer-signature']; const exact = typeof sig === 'string' && /^sha256=[0-9a-f]{64}$/i.test(sig); const provided = Buffer.from( exact ? sig.slice(7) : '', 'hex'); const valid = exact && provided.length === expected.length && crypto.timingSafeEqual(provided, expected); if (!valid) return res.sendStatus(401); const message = JSON.parse(req.body.toString('utf8')); await enqueueOnce(message.occurrenceId, message); res.sendStatus(200);
import { SeatLayer, SeatLayerConflictError, verifyWebhook } from '@seatlayer/server'; const seatlayer = new SeatLayer(process.env.SEATLAYER_SECRET_KEY); // inspect the hold, then book with a stable reference const hold = await seatlayer.inventory.retrieveHold(eventKey, holdId); if (hold.status !== 'active') return conflict(); const order = await createOrderFrom(hold.items); try { await seatlayer.inventory.book(eventKey, { holdId, bookingRef: order.id }); } catch (error) { if (error instanceof SeatLayerConflictError) return voidPayment(order); throw error; // timeouts: retry with the same bookingRef } // verify a delivery (throws on a bad signature) const message = verifyWebhook({ payload: rawBody, signature: req.headers['x-seatlayer-signature'], secret: process.env.WHSEC });
SeatingChartHeadless map for products that build their own cart, timer and controls.
Headless seating chart reference →SeatManagerThe control panel: a live board your organizers use to track and run each event in real time.
See the control panel ↓Set aside exact seats for sponsors, partners, presales or the box office, each with its own access.
See the allocation workflow →Let buyers open a 3D view of the venue. Booking works the same with or without it.
See the view contract ↓PerformanceGroupPickerBuyers pick the same seats across every date in a run. Your checkout and tickets stay yours.
Performance Groups guide →The control panel is a live board for each event. Add it to your admin with the SDK and your organizers watch sales, holds and blocks as they happen, and run the event without leaving your product. Each person sees only the tools you allow.
Your server decides each operator's tools; no secret key reaches the browser. Embedded control panel guide ↗
const picker = new SeatPicker({ container: '#seat-picker', event: eventKey, publicKey, enable3D: true, seatView: true, onBuyerViewChange: ({ view, seatId }) => { syncBuyerRoute({ view, seatId }); }, }); await picker.render(); syncViewToggle(picker.getBuyerView()); // Open 3D and fly to the selected seat; your route restores it. picker.setBuyerView('venue3d', { flyToSeatId: selectedSeat.id });
The same picker keeps the seats, selection and hold. Your app decides whether the buyer sees the map, the 3D overview or the view from one seat.
onBuyerViewChange tells you the current view and seat, so your URL and back button keep working.Install the official JavaScript, React, Vue, Angular or mobile package. Each entry links its registry, matching quickstart and GitHub source. Server SDKs handle trusted bookings and webhooks; REST and OpenAPI cover other backends.
SeatPicker, headless SeatingChart, and real-time inventory. PerformanceGroupPicker handles multi-performance runs; SeasonPicker handles published Fixed Renewable Season Plans.
| Runtime | Install | Resources |
|---|---|---|
| JavaScript / TypeScript | @seatlayer/js | npm ↗JavaScript seat map SDK ↗GitHub ↗ |
| React | @seatlayer/react | npm ↗React seating chart SDK ↗GitHub ↗ |
| Vue 3 | @seatlayer/vue | npm ↗Vue seating chart SDK ↗GitHub ↗ |
| Angular 17+ | @seatlayer/angular | npm ↗Angular seating chart SDK ↗GitHub ↗ |
| Other web frameworks | @seatlayer/js lifecycle | JavaScript SDK integration ↗Source ↗ |
Trusted provisioning, inventory inspection, booking, webhooks and operations. Every backend can use the canonical REST contract, including the published Seasons API.
| Runtime | Install | Resources |
|---|---|---|
| Node.js | @seatlayer/server | npm ↗Node.js booking SDK ↗GitHub ↗ |
| Python | seatlayer | PyPI ↗Python booking SDK ↗GitHub ↗ |
| PHP | seatlayer/seatlayer-php | Packagist ↗PHP booking SDK ↗GitHub ↗ |
| Java | io.seatlayer:seatlayer-java | Maven Central ↗Java booking SDK ↗GitHub ↗ |
| Go | github.com/seatlayer/seatlayer-go | pkg.go.dev ↗Go booking SDK ↗GitHub ↗ |
| Ruby | seatlayer | RubyGems ↗Ruby booking SDK ↗GitHub ↗ |
| .NET | SeatLayer | NuGet ↗.NET booking SDK ↗GitHub ↗ |
| Any backend | REST + OpenAPI | Event API reference ↗Seat booking integration overview ↗ |
Supported native and cross-platform packages for production reserved seating in mobile products.
| Runtime | Install | Resources |
|---|---|---|
| React Native | @seatlayer/react-native | npm ↗React Native seat map SDK ↗GitHub ↗ |
| Flutter | seatlayer | pub.dev ↗Flutter seat map SDK ↗GitHub ↗ |
| iOS / Swift | seatlayer-ios.git via Swift Package Manager | iOS seat map SDK ↗GitHub ↗ |
| Android / Kotlin | io.seatlayer:seatlayer-android via Maven Central | Maven Central ↗Android seat map SDK ↗GitHub ↗ |
Start from a running app: the React and Vite example renders the headless seating chart with best available, holds and a checkout handoff, and the Next.js 15 App Router example adds an /api/hold server route. Add your event key and public key to .env.local and deploy from the README buttons.
From a ready-made picker to a bare map. Every level uses the same live seats and the same checkout handoff.
Selection rules (minimum, consecutive, no single-seat gaps) and hold recovery work at every level. Picker policies · Flutter custom layout · React Native components · JavaScript guide
Turn on WebMCP in the JavaScript browser SDK and a compatible in-browser assistant can describe the event, find seats together within a budget, select them and read the selection. Holding is optional and uses the normal checkout handoff. There is no payment tool: it cannot set trusted prices or complete a booking.
Buyer agent tools ↗seatlayer_describe_eventseatlayer_find_seatsseatlayer_select_seatsseatlayer_get_selectionseatlayer_hold_selectionoptionalEvery buyer gets a snapshot of the seats when the map opens, then live updates as others hold and buy. Holds and bookings for one event are processed in order, so two buyers can never get the same seat. Public pickers start with { event, publicKey }; booking always happens on your server with a secret sk_ key.
// on connect { "type": "snapshot", "seats": { "A-1": "free", "A-2": "booked", "C-14": "free" } } // then, as buyers act { "type": "delta", "changes": [ { "label": "C-14", "status": "held" } ] }
An illustration of how two buyers racing for one seat are handled. Read the live-inventory guide.
Three kinds of key, each with a clear job. The public key loads the picker in the browser. The buyer session holds and releases seats. The secret key, kept on your server, books seats and manages the event. Reuse the same bookingRef if a booking result is unknown, and it will never book twice.
| Credential | Where it lives | Can | Cannot |
|---|---|---|---|
| Event key | Browser, mobile app | Identify one event | Authorize Platform/SDK chart or inventory routes by itself |
pk_test_ pk_live_ | Browser, mobile app | With event, bootstrap Public-sale chart and inventory for a registered origin and matching mode | Read private channels, identify a buyer, inspect a hold, or book |
bse_ (public bootstrap) | SDK memory; minted by SeatLayer | Render, subscribe, select, hold and release Public-sale inventory for one event and origin | Persist safely, access private channels, inspect holds, or book |
bse_ (private audience) | SDK memory; returned by your backend token provider | Expose the exact event, origin and private channels your server authorizes | Book, persist safely, or stand in for an account secret |
dse_ | Embedded Designer; minted by your server | Open one origin-, workspace- and chart-scoped Designer session with explicit actions | Operate SeatManager, buyer inventory or account APIs |
mse_ | SeatManager; minted by your server | Open one event-scoped operator surface with explicit capabilities | Open the Designer, book as an account, or provision resources |
sk_test_ | Your server | Everything on sandbox events; livemode: false, no credits used | Touch a live event (403 mode_mismatch) |
sk_live_ | Your server | Inspect holds, book, block, provision, report on live events | Touch a sandbox event |
| Endpoint | Auth | Does |
|---|---|---|
| POST/pub/events/:key/bootstrap | public key + Origin | Public chart + inventory + internal buyer session |
| WS/pub/events/:key/subscribe | bse_ | Snapshot + delta stream |
| POST/pub/events/:key/hold | bse_ | Hold seats or GA atomically |
| POST/pub/events/:key/release | bse_ | Release a hold |
| POST/pub/events/:key/best-available | bse_ | Find and hold seats together |
| POST/v1/events/:key/book | secret key | Finalize a sale idempotently |
| POST/v1/events/:key/block | secret key | Block or unblock house seats |
| POST/v1/seasons | secret key | Create a Fixed Renewable Season and immutable Plan |
| POST/v1/webhooks | session | Manage webhook endpoints |
| POST/v1/keys | session | Create, rotate or revoke keys |
// POST /v1/events/:key/book with a different bookingRef, // an expired hold or missing inventory: nothing is newly booked HTTP 409 { "error": "conflict", "conflicts": [ { "label": "A-12", "status": "booked" } ] } // same holdId + bookingRef after a timeout: idempotent replay HTTP 200 { "ok": true, "booked": [] }
bookingRefis the idempotency key. One immutable reference per order; a replay never books or charges twice.409is a real answer, not a retry. Refresh, reselect, or surface the conflicting labels. Mutations are atomic.429on holds carries retryAfterSeconds. Budgeted per secret key, not per IP; releases are never rate-limited. Server SDKs throw a typed RateLimitError.Fixed Renewable Seasons add structure, Plan, sales, hold, booking, amendment and renewal lifecycle webhooks under season.*. Read the Seasons API contract.
Your coding assistant can read our docs and SDK reference. Designer MCP lets an authorized AI client build a whole venue from one Venue Spec, then edit it for your review. Publishing stays with a person. Buyer-side assistants are covered with the seat picker above.
Your backend checks workspace and chart ownership, then mints a short-lived dse_… session for an exact origin. EmbeddedDesigner handles iframe lifecycle, validated messages, relaunch and teardown.
Your backend mints an event-scoped mse_… token with explicit capabilities. SeatManager supplies the packaged board; ManageApi exposes the same scoped operations for custom interfaces.
# Portable integration guidance git clone https://github.com/seatlayer/seatlayer-ai-toolkit.git cd seatlayer-ai-toolkit node scripts/install.mjs --help # Read-only integration diagnostics node scripts/doctor.mjs /path/to/project # Canonical context for any coding agent https://docs.seatlayer.io/llms.txt
A compatible MCP client begins at SeatLayer’s authorization screen. The agent does not receive an organization API key.
An agent writes a short JSON file that says what the venue has (ticket types and prices, a stage, rows, curved rows, tables, standing, booths, bars, exits, signs, a dance floor) and where, in words. build_from_venue_spec builds it in one call. With checkOnly it reports every problem by field and saves nothing. The file never holds coordinates; SeatLayer places everything.
?tools=buildhttps://mcp.seatlayer.io/mcp?tools=build shows the AI 63 tools for building, editing, pricing, checking and publishing instead of all 132, so each turn carries less. The plain URL keeps every tool. Either way, the grant decides what a call may do, and publishing needs a person's approval.
{
"$schema": "https://docs.seatlayer.io/schemas/venue-spec-v1.json",
"seatlayerVenueSpec": 1,
"name": "Riverside Theatre",
"categories": [
{ "name": "Premium", "price": 80 },
{ "name": "Standard", "price": 55 }
],
"stage": { "kind": "arc" },
"items": [
{ "type": "rows", "rowCount": 10,
"seatsPerRow": 24, "blocks": 2,
"category": "Premium", "section": "Stalls" },
{ "type": "curvedRows", "rowCount": 5,
"seatsPerRow": 32, "category": "Standard",
"section": "Rear Stalls" },
{ "type": "landmark", "role": "entrance",
"placement": "rear" },
{ "type": "landmark", "role": "bar",
"near": "Entrance", "side": "left" }
]
}
Test mode is free, with no time limit and no card. On a live account you pay only for confirmed sold seats: rendering, holds, releases, house seats and unsold seats never use a credit. Your checkout and payments stay yours, so there is no revenue share.
After that, credits start at $0.10 per sold seat, around half seats.io Silver’s starting rate. Compare seat map API costs →
See the complete credit ladder →Need something that is not listed? If you need a payment provider we do not support yet, or an API capability you cannot find here, tell us: gateway and feature requests come straight to the team. Send a request →
No time limit and no card. Prove the complete integration before switching to live.
Use them for real sales, every month, with no subscription.
Per confirmed sold seat. $50 buys 500 credits; the rate falls to a $0.05 floor at volume.
Purchased credits stay available until a confirmed sold seat uses them.
Still have a question? or email hello@seatlayer.io.
SeatLayer targets reserved or assigned seating. GA capacity is supported only as an area inside a reserved-seat chart; pure-GA and chartless events are not supported.
Yes. Flutter, React Native, iOS and Android support native buyer UI and layout composition around the shared WebView seat renderer. Use the ready picker, customize its controls, compose your layout or build around the raw map. This is native control over the buying interface, not a claim of a fully native map engine.
Yes. Standard Platform/SDK organizations can go live with $0 upfront and 100 free confirmed sold-seat credits each month. Buy non-expiring credits when needed, starting at $50 for 500. Your checkout, payment processing and any separately priced add-ons remain your responsibility.
Stable web packages cover JavaScript and TypeScript, React, Vue 3 and Angular 17+. Official server SDKs cover Node.js, Python, PHP, Java, Go, Ruby and .NET, while REST and OpenAPI support other backends. Supported production mobile SDKs cover React Native, Flutter, iOS and Android.
SeatPicker is the complete buyer surface: map, selection, cart, tiers and GA, hold timer, responsive UI, optional 3D and onCheckout handoff. SeatingChart is the headless map for a product that wants to build its own cart, timer, confirmation and surrounding controls.
No. Pass { event, publicKey } for a public Platform/SDK event. The SDK calls SeatLayer directly and receives chart, inventory and a short-lived public-only bse_ session together; that internal session stays in memory. For a login, presale or private channel, supply a buyer-access token provider instead of publicKey. The provider calls your backend for the narrower bse_ session.
Yes. SeatLayer publishes LLM-readable Markdown documentation and an open-source AI Toolkit with live-doc routing, a read-only integration doctor and verification guidance. The canonical documentation, your repository architecture and production testing remain the source of truth.
Designer MCP lets a compatible MCP client work through one authorized chart scope. SeatLayer's API still controls permissions, confirmation, chart writes, canonical revisions, validation, and evidence; publication remains a separate human-controlled action.
Yes. Each buyer’s map loads the event’s seats and then gets every change live. Two buyers can never get the same seat, even when thousands click at once. When 30 requests raced for one seat in our published test, one hold succeeded and every other request was told the seat was taken.
A seat map API lets a ticketing product show a venue’s seats, see which are free right now, and hold or sell specific ones. SeatLayer covers all three with web, mobile and server SDKs plus REST and OpenAPI. The browser loads the map with an event id and a public key, your server books seats with a secret key, and live updates keep every buyer’s map current. Comparing providers? See how SeatLayer compares with seats.io.
Add the seat picker to your web or mobile app with an event id and a public key; no backend call is needed for a public sale. For private sales, your server hands the picker a buyer token instead. Your server then books seats with a server SDK or REST and a secret key, and listens for signed webhooks. If a booking result is unclear, retry with the same bookingRef and it will never book twice. Test mode is free, with no time limit and no card. The platform SDK quickstart walks through it end to end.
Yes. Holding and releasing seats are first-class operations: the browser uses its in-memory bse_ buyer session to select, hold and release, a trusted server-only sk_ key can inspect holds and book them, and holds, releases and expiry all arrive as inventory deltas on every connected session. Holding a seat is not a sale. Rendering charts, holds, releases, house blocks and unsold inventory never consume a credit, and only a confirmed sold seat does. See seat map API pricing.
Create test keys, hold and book a seat through your backend, and check a signed webhook before you go live. Want to look first? browse the live seat map demos, or hold a run of performances together in the Performance Group demo.