---
title: "Embedded Designer"
description: "Let organizers create and edit charts inside your product with workspace-scoped, reviewable browser sessions."
---

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

Embed the full SeatLayer Designer in your CMS without creating a SeatLayer login
for every organizer. Your server owns tenancy and mints a short-lived session;
the SDK owns iframe lifecycle and message validation.

## End-to-end flow

1. Create or select a chart in the organizer workspace.
2. Authorize the current host user and decide their Designer authority.
3. Mint a `dse_…` session from your backend.
4. Mount the returned `designerUrl`.
5. React to saved, published, close, and error events.
6. Replace the session before or after expiry.
7. Revoke it when access ends.

## Mint from the server

```ts title="server/designer-session.ts"
export async function createDesignerSession(input: {
  workspaceId: string;
  chartId: string;
  allowedOrigin: string;
}) {
  const response = await fetch(
    "https://api.seatlayer.io/v1/designer/sessions",
    {
      method: "POST",
      headers: {
        authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
        "content-type": "application/json",
      },
      body: JSON.stringify({
        ...input,
        authority: "edit",
        mode: "safe",
        expiresInSeconds: 3600,
      }),
    },
  );

  if (!response.ok) throw new Error("Unable to open Designer");
  return response.json();
}
```

Use `read-only` for review, `edit` for draft work, or `publish` only when the host
user is allowed to release a chart. Safe mode is a useful default for delegated
venue editors.

## Mount with JavaScript

```js title="browser/designer.js"
import { EmbeddedDesigner } from "@seatlayer/js";

let editor;

async function mountDesigner() {
  const { session } = await fetch("/api/designer-session", {
    method: "POST",
  }).then((response) => response.json());

  editor = new EmbeddedDesigner({
    container: "#designer",
    designerUrl: session.designerUrl,
    expectedChartId: session.chartId,
    expectedWorkspaceId: session.workspaceId,
    height: "fill",
    minHeight: 560,
    showLoadingState: true,
    onReady: () => markEditorReady(),
    onSaved: (message) => saveHostRevision(message.chartId),
    onPublished: (message) => refreshChart(message.chartId),
    onClose: () => closeEditor(),
    onError: (message) => reportSafeError(message.code),
    onRequestRelaunch: async () => {
      const next = await fetch("/api/designer-session", {
        method: "POST",
      }).then((response) => response.json());
      editor.setDesignerUrl(next.session.designerUrl);
    },
  });

  editor.mount();
}
```

`height: "fill"` detects whether the container has a definite height. It fills
that box or uses the remaining viewport height, with a minimum of 480 by
default. A numeric height opts into fixed-pixel behavior and legacy resize
messages.

## React

```tsx title="components/VenueEditor.tsx"
import { EmbeddedDesigner } from "@seatlayer/react";

export function VenueEditor({ session, relaunch }) {
  return (
    <EmbeddedDesigner
      designerUrl={session.designerUrl}
      expectedChartId={session.chartId}
      expectedWorkspaceId={session.workspaceId}
      onSaved={({ chartId }) => refreshVenue(chartId)}
      onPublished={({ chartId }) => activateRevision(chartId)}
      onRequestRelaunch={relaunch}
    />
  );
}
```

## Message safety

The SDK accepts only documented `seatlayer.designer.*` messages from the exact
iframe window and Designer origin. `expectedChartId` and
`expectedWorkspaceId` add host-side resource checks.

| Event | Host response |
|---|---|
| `ready` | Remove outer loading state |
| `saved` | Update host draft status |
| `published` | Refresh event/chart state and show release result |
| `close` | Navigate away or collapse editor |
| `error` | Show safe recovery UI; do not expose raw tokens |

Do not build business logic from the legacy resize message.

## Session replacement

The SDK can proactively relaunch when `onRequestRelaunch` exists. It uses the
session expiry received from the iframe and replaces the iframe with the new
fragment URL. A slept device that wakes after expiry gets one automatic recovery
attempt before the error card appears.

Call `destroy()` when the host unmounts the editor.

## Publication behavior

Publishing updates the chart. Existing events are separately protected:
pristine events may auto-refresh; events with inventory state can require an
explicit map update, and an update fails if booked or held labels would vanish.
Surface this result to the organizer.

## Verification

- [ ] The secret key is never present in browser code or network responses.
- [ ] Workspace and chart ownership are checked before minting.
- [ ] Origin matching is exact in production.
- [ ] Authority matches the host user's role.
- [ ] Safe mode and feature policy match the editing job.
- [ ] Save, publish, close, expiry, relaunch, and revoke flows are tested.
- [ ] The container works at narrow widths and has a usable minimum height.
- [ ] Host code destroys the embed during teardown.

See [embed sessions](/platform/embed-sessions),
[publishing and versioning](/designer/publishing-and-versioning), and
[Designer MCP](/agents/designer-mcp).

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