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
- Create or select a chart in the organizer workspace.
- Authorize the current host user and decide their Designer authority.
- On authenticated intent or page load, start your backend’s
dse_…session request while the SDK and editor shell load. - Mount the returned
designerUrlas soon as both are ready. - React to saved, published, close, and error events.
- Replace the session before or after expiry.
- Revoke it when access ends.
Mint from the server
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
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
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.