Skip to content

Embedded Designer

Let organizers create and edit charts inside your product with workspace-scoped, reviewable browser sessions.

Updated View as Markdown

Embed the full SeatLayer Designer in your CMS without creating a SeatLayer login for every organizer. Your server owns tenancy and authorizes the request; SeatLayer mints the short-lived session, and the SDK owns iframe lifecycle and message validation. The seat map designer page shows what organizers get inside that session.

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. On authenticated intent or page load, start your backend’s dse_… session request while the SDK and editor shell load.
  4. Mount the returned designerUrl as soon as both are ready.
  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

server/designer-session.tsts
export async function createDesignerSession(input: {
  workspaceId: string;
  chartId: string;
  allowedOrigin: string;
}) {
  const response = await fetch(
    "https://api.seatlayer.io/v1/designer/sessions",
    {
      method: "POST",
      cache: "no-store",
      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.

SeatLayer generates and hash-registers the dse_… token. Your backend must first authenticate the organizer, verify tenant/workspace/chart ownership, choose the authority and exact origin, and then call this endpoint with sk_…. Never call it from browser code, expose sk_…, or try to mint a Designer token client-side. Return the scoped session through a Cache-Control: no-store host response.

Do not wait for the final Open Designer click if the user is already authorized. Start the host session request on route intent or authenticated page load and keep its promise in memory. Alternatively, include the returned designerUrl in an authenticated, no-store server-rendered bootstrap. This moves the required server trust boundary off the click path without weakening it.

Mount with JavaScript

browser/designer.jsjs
let editor;

async function requestDesignerSession() {
  const response = await fetch("/api/designer-session", {
    method: "POST",
    credentials: "same-origin",
    cache: "no-store",
  });
  if (!response.ok) throw new Error("Unable to open Designer");
  return response.json();
}

// Start both jobs when the authenticated editor route loads. Draw your own
// shell/skeleton immediately; the iframe still waits for designerUrl.
showDesignerShell();
const sdkPromise = import("@seatlayer/js");
const initialSessionPromise = requestDesignerSession();

async function mountDesigner() {
  const [{ EmbeddedDesigner }, { session }] = await Promise.all([
    sdkPromise,
    initialSessionPromise,
  ]);

  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 requestDesignerSession();
      editor.setDesignerUrl(next.session.designerUrl);
    },
  });

  editor.mount();
}

void mountDesigner();

The Designer iframe cannot fetch the chart until the dse_… session exists. Once mounted, the SeatLayer app already races its authenticated chart/session read against the lazy Designer bundle. The host-side win is therefore to overlap session creation with SDK import and shell rendering, not to move session issuance into the browser.

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

components/VenueEditor.tsxtsx
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}
    />
  );
}

Resolve session in the authenticated route loader or server component where possible, set the response to no-store, and render the shell while the client component bundle loads. Keep the raw session only in memory.

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, publishing and versioning, and Designer MCP.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close