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.
Browser, server and mobile SDKs, server-authoritative live inventory and signed webhooks. Your checkout, payments, orders and tickets stay yours.
Test mode is free.·100 free credits every month·then prepaid credits from $0.10 down to $0.05 per confirmed sold seat·credits never expire·no subscription
$ npm install @seatlayer/js
$ npm install @seatlayer/react
The picker hands your server an opaque holdId. Your server prices, charges and books with a stable bookingRef; a signed webhook confirms the outcome. Charts, live availability, holds and inventory booking are SeatLayer’s; checkout, payment, orders and tickets are yours.
import { SeatPicker } from '@seatlayer/js'; const picker = new SeatPicker({ container: '#seat-picker', event: 'ev_9f3a', // change this at runtime 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 reference →Allocate exact seats to public, sponsor, partner, presale, or box-office audiences with scoped access and durable attribution.
See the allocation workflow →Open, restore and deep-link the synchronized venue view without changing the booking boundary.
See the view contract ↓PerformanceGroupPickerFor a fixed multi-performance run, buyers keep the same assigned seats or choose a matching allocation for each listed date. Your checkout and tickets stay in your product.
Performance Groups guide →const picker = new SeatPicker({ container: '#seat-picker', event: eventKey, 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 availability, selection and hold context while your application decides whether the buyer sees Map, 3D overview or a targeted seat.
onBuyerViewChange supplies view and optional seat state; your route and history remain yours.Every route is available over REST and the OpenAPI reference; each package links to the registry it ships on.
SeatPicker, headless SeatingChart, real-time inventory and platform components. PerformanceGroupPicker supports fixed multi-performance runs.
Trusted provisioning, inventory inspection, booking, webhooks and operational APIs.
| Runtime | Install | Resources |
|---|---|---|
| Node.js | @seatlayer/server | npm ↗Docs ↗GitHub ↗ |
| Python | seatlayer | PyPI ↗Docs ↗GitHub ↗ |
| PHP | seatlayer/seatlayer-php | Packagist ↗Docs ↗GitHub ↗ |
| Java | io.seatlayer:seatlayer-java | Maven Central ↗Docs ↗GitHub ↗ |
| Go | github.com/seatlayer/seatlayer-go | pkg.go.dev ↗Docs ↗GitHub ↗ |
| Ruby | seatlayer | RubyGems ↗Docs ↗GitHub ↗ |
| .NET | SeatLayer | NuGet ↗Docs ↗GitHub ↗ |
| Any backend | REST + OpenAPI | API reference ↗Guides ↗ |
Supported native and cross-platform packages for production reserved seating in mobile products.
Inspect every public SDK, example and integration tool from the SeatLayer organization. The inventory service itself remains private.
VIEW ALL ON GITHUB ↗Each buyer session subscribes at /pub/events/:key/subscribe, receives a seat-status snapshot, then applies deltas for holds, bookings, releases, blocks and expiry. Trusted booking and operations use a mode-scoped secret.
// 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" } ] }
This is an illustrated concurrency contract, not a live external transaction. Read the live-inventory guide.
Public buyer routes use the event key. Trusted routes use Authorization: Bearer with a mode-scoped secret. Reuse the same bookingRef when a booking result is unknown.
| Credential | Where it lives | Can | Cannot |
|---|---|---|---|
| Event key | Browser, mobile app | Render, subscribe, select, hold, release | Book, read other events |
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 |
dse_ mse_ bse_ | Browser, minted by your server | One origin, short-lived: designer session, operator board, buyer audience | Book; stand in for a secret key |
| Endpoint | Auth | Does |
|---|---|---|
| WS/pub/events/:key/subscribe | event key | Snapshot + delta stream |
| POST/pub/events/:key/hold | event key | Hold seats or GA atomically |
| POST/pub/events/:key/release | event key | Release a hold |
| POST/pub/events/:key/best-available | event key | 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/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.Embed the venue workflows your product needs, give coding agents the same canonical documentation humans read, or connect a compatible MCP client to one authorized chart. The API still owns policy, confirmation, chart writes, revisions, and evidence.
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 workflow for Codex git clone https://github.com/seatlayer/seatlayer-ai-toolkit.git cd seatlayer-ai-toolkit node scripts/install.mjs --target codex # 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.
Test mode is free, with no time limit and no card. On a live account, rendering charts, holds, releases, house blocks and unsold inventory never consume a credit — only a confirmed sold seat does. Your product keeps its own checkout, payments and orders, so no revenue share applies.
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.
Included on every live account, with no subscription and no minimum commitment.
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.
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.
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 Preview 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 connected buyer session receives an event-scoped snapshot followed by inventory deltas. One state owner per event serializes transitions; in the released 30-way race test for one contested seat, one hold succeeded and the other requests received conflicts.
Create credentials, connect one event, run a hold through your backend, confirm inventory booking and verify a signed webhook before switching to live mode. Prefer to inspect first? Explore all demos, or hold a run of performances together in the Performance Group demo.