---
title: "Embedded control room"
description: "Give authorized operators a live, capability-scoped SeatManager board inside your own application."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.seatlayer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedded control room

`SeatManager` is a same-document operator board—not an iframe. It renders the
event map, subscribes to live inventory, shows occupancy and activity, and
exposes controlled blocking, section, cancellation, and reporting tools.

## Trust boundary

Your backend authorizes the operator and mints an event-scoped `mse_…` token.
The browser receives only that token. Never put an account secret key in the
page.

```text title="control-room-flow.txt"
Operator browser -> your backend: request event access
Your backend -> SeatLayer: mint mse_ token for event + origin + capabilities
Your backend -> browser: token + expiry
Browser SeatManager -> SeatLayer: live reads and scoped actions
```

## Mint a manage session

```bash
curl -sX POST \
  "https://api.seatlayer.io/v1/events/ev_9f3a/manage-sessions" \
  -H "authorization: Bearer $SEATLAYER_SECRET_KEY" \
  -H "content-type: application/json" \
  -d '{
    "allowedOrigin": "https://admin.example.com",
    "capabilities": [
      "event:view",
      "event:block",
      "event:reports"
    ],
    "expiresInSeconds": 3600
  }'
```

Add `event:cancel` only for operators allowed to return booked inventory to
sale. Box-office booking stays a trusted server action.

## Mount the board

```js title="browser/control-room.js"
import { SeatManager } from "@seatlayer/js";

const session = await fetch("/api/events/ev_9f3a/manage-session", {
  method: "POST",
}).then((response) => response.json());

const room = new SeatManager({
  container: "#control-room",
  eventKey: "ev_9f3a",
  token: session.token,
  tokenExpiresAt: session.expiresAt,
  mode: "view",
  keepLiveWhileHidden: true,
  followLive: false,
  onTokenRefresh: async () => {
    const next = await fetch("/api/events/ev_9f3a/manage-session", {
      method: "POST",
    }).then((response) => response.json());
    return { token: next.token, expiresAt: next.expiresAt };
  },
  onError: (error) => showOperatorError(error),
});

await room.render();
```

Give the container a real height. SeatManager fills that box and watches it with
`ResizeObserver`. Fullscreen uses the browser Fullscreen API.

## Operator modes

| Mode | Purpose | Capability |
|---|---|---|
| `view` | Live occupancy, activity, momentum, presence | `event:view`; revenue also needs `event:reports` |
| `inspect` | Read the current state and context of one seat | `event:view` |
| `block` | Select and block/unblock individual inventory | `event:block` |
| `sections` | Open, close, hide, or schedule sections/zones | `event:block` |

Following live activity is off by default so a busy event does not repeatedly
move the operator's camera. Heat overlays express sales velocity without
replacing chart category colors.

## Common actions

```ts title="operator/actions.ts"
room.setMode("block");
room.selectSection("sec-stalls");

await room.block(undefined, {
  releaseAt: Date.parse("2026-08-01T19:00:00Z"),
  reason: "Soundcheck hold",
});

await room.unblock(["A-3"]);
await room.unblockAll();

await room.cancelBooking(["C-14", "C-15"], "order_5567");
await room.setHoldTtl(10 * 60 * 1000);

const report = await room.getReport();
const log = await room.getLog({ limit: 50 });
```

Cancellation changes inventory only; your application owns refund policy and
payment execution. The booking reference prevents releasing a newer sale.

## Long-running boards

Pass both `tokenExpiresAt` and `onTokenRefresh`. SeatManager proactively
requests a replacement and also retries once after an authorization failure.
Your refresh endpoint must repeat the full host-user authorization check rather
than accepting the expired token as proof.

`keepLiveWhileHidden: true` forces a fresh paint after live deltas when browsers
throttle background animation frames. Use it for a wallboard; consider battery
and resource costs on laptops.

## Custom operator UI

Use `ManageApi` when the packaged board does not match your workflow. It exposes
the same token-authenticated reads and mutations: availability, block/unblock,
cancellation, hold TTL, report, control-room snapshot, audit log, and CSV Blob.
Keep capability rules identical to SeatManager.

## Verification

- [ ] The backend authorizes event and tenant before every token mint.
- [ ] The token carries only necessary capabilities.
- [ ] The board has a bounded, responsive container.
- [ ] Token rotation is tested across laptop sleep and network loss.
- [ ] Revenue is blank/stale rather than reconstructed when reports are unavailable.
- [ ] Cancellation is paired with an explicit refund workflow.
- [ ] Conflict errors ask the operator to reload or reselect.
- [ ] `destroy()` runs during unmount and logout.
- [ ] Revocation occurs when access ends.

Continue to the [SeatManager reference](/platform/seat-manager),
[embed sessions](/platform/embed-sessions), and
[reports](/server-api/reports).

Source: https://docs.seatlayer.io/platform/embedded-control-room/index.mdx
