Document / EN
Quick start — embed in 3 steps
Get a working checkout map without reading the full guide.
Try it first, no account needed
- Designer playground: the real map designer in your browser. Start from a template, edit with undo and redo, check the validation list, preview the buyer view and export the chart as JSON (and import it back). The draft stays in your browser and nothing is sent to SeatsChart; after you sign up, New chart offers to turn it into a chart of your workspace.
- Demo shop: a made-up concert shop running the production buyer viewer, with category prices, ticket types, a hold timer, best available and a fake checkout. Seats, holds and other buyers are simulated in the browser through the viewer's map-only mode, so nothing is reserved or charged.
1. Mint a short-lived viewer token (your server)
Your server asks SeatsChart for a buyer token with its partner API key. You never need the platform JWT secret.
import { SeatsChart } from "@seatschart/sdk";
const sc = new SeatsChart({
apiBase: process.env.SEATSCHART_API_BASE!,
apiKey: process.env.SEATSCHART_API_KEY!, // sk_live_… server only
});
const { token } = await sc.viewerTokens.create({ ttlSeconds: 900 }); // 60–3600 s, default 900Same call with fetch:
const res = await fetch(`${process.env.SEATSCHART_API_BASE}/api/viewer-tokens`, {
method: "POST",
headers: { "Content-Type": "application/json", "x-api-key": process.env.SEATSCHART_API_KEY! },
body: JSON.stringify({ ttlSeconds: 900 }),
});
const { token } = await res.json(); // { token, tokenType: "Bearer", expiresInSeconds, expiresAt }Pass it only as ?token= on the viewer URL (or the React token prop). No secretKey or API key in the browser.
2. Embed the viewer + listen for hold
<iframe
title="Seat map"
src="https://YOUR_WEB/viewer/EVENT_UUID?token=JWT&parent_origin=https%3A%2F%2FYOUR_STORE&embed=1"
width="100%"
height="640"
style="border:0"
></iframe>Or React:
import { SeatChart } from "@seatschart/react";
<SeatChart
webBase="https://YOUR_WEB"
eventId="EVENT_UUID"
parentOrigin="https://YOUR_STORE"
token={jwt}
onChartRendered={() => {}}
onHoldFailed={(e) => console.error(e.message)}
onSessionHold={(e) => {/* route to your PSP with e.holdToken + e.seatIds */}}
/>Or Vue (@seatschart/vue) or Angular (@seatschart/angular):
<SeatChart web-base="https://YOUR_WEB" event="EVENT_UUID" :token="jwt"
@hold-failed="(e) => console.error(e.message)" @session-hold="(e) => pay(e.holdToken, e.seatIds)" /><seatschart-chart webBase="https://YOUR_WEB" event="EVENT_UUID" [token]="jwt"
(holdFailed)="showError($event.message)" (sessionHold)="pay($event.holdToken, $event.seatIds)" />Register your store origin under Settings → Embed domains.
Dashboard try-map: Events → open the eye / viewer icon → same flow on your workspace (/dashboard/viewer/{eventId}).
3. Quote → pay → confirm (partner API key)
The event needs a currency (Event Manager → currency) before it can be quoted. On your server:
// a) Freeze the server-calculated price of the hold (valid 10 minutes).
const quote = (await sc.quotes.create({ eventId, userId, holdToken })) as {
id: string; totalCents: number; currency: string; expiresAt: string;
};
// b) Charge exactly quote.totalCents in quote.currency on YOUR PSP.
const charge = await psp.charge({ amount: quote.totalCents, currency: quote.currency });
// c) Confirm with the quote and your payment reference.
await sc.seats.confirm({
eventId,
seatIds,
userId,
holdToken,
quoteId: quote.id,
externalPaymentId: charge.id,
paidAmountCents: quote.totalCents,
paidCurrency: quote.currency,
});Never call quote or confirm with the iframe buyer JWT.
When something fails
| Symptom | Likely cause |
|---|---|
Blank map / chart.renderingFailed | Expired or invalid viewer token, event of another workspace, unpublished event, API down |
No postMessage after hold | Missing / wrong parent_origin (must match store origin exactly) |
hold.failed | Seat locked by someone else or booking-rule limit |
| Viewer token 403 | Called with a buyer JWT instead of x-api-key |
Quote 409 quote_conflict | Event has no currency, hold expired or Idempotency-Key reused with another request |
| Confirm 401/403 | Wrong x-api-key or confirm attempted from browser JWT |
Confirm 409 quote_required | No quoteId — create a quote for the hold first |
Confirm 409 quote_mismatch | Quote of another hold/user/event, or externalPaymentId / paidAmountCents / paidCurrency differ from the quote |
| Confirm 409 / expired | Hold TTL or quote (10 min) elapsed — reconcile the payment and ask the buyer to select again |
Next: Server integration · Integration guide (full embed params) · Map designer
Stay up to date
Product updates are published on the changelog, with an RSS feed and a Markdown copy for tools and agents. Each entry names the package versions it ships in (@seatschart/embed, @seatschart/react, @seatschart/react-native, @seatschart/vue, @seatschart/angular).
Embedding the seat map for buyers? Read Buyer UX guidelines before you launch.
