# Reserved seating SDK and API for your ticketing product.

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

## SeatLayer owns inventory. Your product owns the customer.

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.

- 1 · render
- 2 · inspect + book
- 3 · verify webhook
- 2 + 3 · with @seatlayer/server
```
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 });
```

### SeatPicker

Complete buyer surface for the fastest production route.

### SeatingChart

Headless map for products that build their own cart, timer and controls.

### Sales channels

Allocate exact seats to public, sponsor, partner, presale, or box-office audiences with scoped access and durable attribution.

### Optional 3D

Open, restore and deep-link the synchronized venue view without changing the booking boundary.

### PerformanceGroupPicker

For 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.

```
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 });
```

### 3D changes the view—not the inventory owner.

The same picker keeps availability, selection and hold context while your application decides whether the buyer sees Map, 3D overview or a targeted seat.

## Use your stack. Keep the same platform contract.

Every route is available over REST and the OpenAPI reference; each package links to the registry it ships on.

### Web buyer SDKs

SeatPicker, headless SeatingChart, real-time inventory and platform components. PerformanceGroupPicker supports fixed multi-performance runs.

| Runtime | Install | Resources |
| --- | --- | --- |
| JavaScript / TypeScript | @seatlayer/js | npm ↗Docs ↗GitHub ↗ |
| React | @seatlayer/react | npm ↗Docs ↗GitHub ↗ |
| Vue 3 | @seatlayer/vue | npm ↗Docs ↗GitHub ↗ |
| Angular 17+ | @seatlayer/angular | npm ↗Docs ↗GitHub ↗ |
| Other web frameworks | @seatlayer/js lifecycle | Integration guide ↗Source ↗ |

### Server SDKs

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 ↗ |

### Mobile buyer SDKs

Supported native and cross-platform packages for production reserved seating in mobile products.

| Runtime | Install | Resources |
| --- | --- | --- |
| React Native | @seatlayer/react-native | npm ↗Docs ↗GitHub ↗ |
| Flutter | seatlayer | Package ↗GitHub ↗ |
| iOS / Swift | seatlayer-ios.git via Swift Package Manager | Docs ↗GitHub ↗ |
| Android / Kotlin | com.github.seatlayer:seatlayer-android via JitPack | Docs ↗GitHub ↗ |

Inspect every public SDK, example and integration tool from the SeatLayer organization. The inventory service itself remains private.

## Snapshot first. Deltas after. Trusted decisions stay server-side.

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.

## A compact API surface with signed delivery.

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.
- 9is a real answer, not a retry. Refresh, reselect, or surface the conflicting labels. Mutations are atomic.
- 9on holds carries retryAfterSeconds. Budgeted per secret key, not per IP; releases are never rate-limited. Server SDKs throw a typed RateLimitError.

### Eight published event types

- seat.bookeda seat sold
- seat.releasedhold released
- seat.blockedheld back
- hold.createdbuyer selected
- hold.extendedexpiry moved
- hold.expiredtimed out
- event.createdevent opened
- event.soldoutlast seat gone

- SignatureX-SeatLayer-Signature: sha256=<hex>
- AlgorithmHMAC-SHA256 over the raw body
- Attempt10-second attempt timeout
- Retriesfirst attempt plus up to three retries
- Lognewest 200 attempts · 30-day cleanup

## Let people and agents help without handing over the keys.

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.

### 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.

### Run live inventory tools inside your admin

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
```

### Connect

A compatible MCP client begins at SeatLayer’s authorization screen. The agent does not receive an organization API key.

## Build and test for nothing. Pay when a seat sells.

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.

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 →

### TEST MODE Free

No time limit and no card. Prove the complete integration before switching to live.

### EVERY MONTH 100 free credits

Included on every live account, with no subscription and no minimum commitment.

### BEYOND THE ALLOWANCE $0.10 → $0.05

Per confirmed sold seat. $50 buys 500 credits; the rate falls to a $0.05 floor at volume.

### UNUSED BALANCE Never expires

Purchased credits stay available until a confirmed sold seat uses them.

## Questions before you integrate.

### Which frameworks and languages does the SeatLayer SDK support?

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.

### What is the difference between SeatPicker and SeatingChart?

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.

### Can a coding agent help integrate SeatLayer?

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.

### Can an external AI agent work on a SeatLayer chart?

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.

### Is there a real-time seat inventory API?

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.

## Prove your complete integration in test mode.

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.

---

_Machine-readable site index: [/llms.txt](https://seatlayer.io/llms.txt) · full corpus: [/llms-full.txt](https://seatlayer.io/llms-full.txt)_
_Source page: [https://seatlayer.io/developers/](https://seatlayer.io/developers/) · Questions: [Contact SeatLayer](https://seatlayer.io/contact/) · hello@seatlayer.io_
