Skip to content

Performance Group picker

Let a buyer choose seats across a fixed run of performances, hold every allocation together, and hand the group to your checkout.

Updated View as Markdown

PerformanceGroupPicker is the complete buyer seating step for a fixed run of two to eight performances that share one published seating chart. It is available in @seatlayer/js 0.64.0 and later.

It is not a season-ticket, subscription, pass, or buyer-selectable date bundle. The buyer purchases every performance shown by the picker. Your application still owns the offer, checkout, payment, commercial order, ticket delivery, and support.

Choose the selection mode

Mode Buyer experience Use it when
same_seat The buyer selects once; those exact seats are held for every included performance. The run promises the same seat each night.
per_performance The buyer chooses an equal number of seats on each dated tab; seats may differ by performance. The buyer needs the same party size but not the same physical seats.

Both modes create one all-or-nothing group hold. The browser never makes a separate hold for each Event.

Before you render

Your trusted backend must create the Performance Group, activate it, and mint a short-lived bsg_… buyer-access session for the exact browser origin. See the Performance Groups API for that server-side setup.

Return only { token, expiresAt } to the browser. Do not put the token in a URL, persistent browser storage, analytics, or logs.

Same seat for every performance

browser/group-picker.tsts
import { PerformanceGroupPicker } from "@seatlayer/js";

const picker = new PerformanceGroupPicker({
  container: "#seat-picker",
  performanceGroup: "pg_opening_weekend",
  buyerAccessTokenProvider: async () => {
    const response = await fetch("/api/seatlayer/group-access", {
      method: "POST",
      credentials: "same-origin",
      cache: "no-store",
    });
    if (!response.ok) throw new Error("Unable to open seat selection");
    return response.json(); // { token: "bsg_…", expiresAt }
  },
  onCheckout: (handoff) => {
    // This starts your checkout. It does not book inventory yet.
    startCheckout({
      performanceGroup: handoff.performanceGroup.key,
      holdId: handoff.holdId,
      operationId: handoff.operationId,
    });
  },
  onError: (error) => reportSeatSelectionError(error),
});

await picker.render();

The picker presents the complete date list, one shared chart, an accessible selection summary, and one Hold seats for all performances action. When the hold completes, the primary action becomes Continue to checkout.

Different seats on each date

Use per_performance only when your offer permits different seats. The picker reuses one chart and shows dated tabs; it requires the configured party size on every performance before the hold action becomes available.

browser/per-performance-picker.tsts
const picker = new PerformanceGroupPicker({
  container: "#seat-picker",
  performanceGroup: "pg_three-night-run",
  selectionMode: "per_performance",
  numberOfPlacesToSelect: 2,
  buyerAccessTokenProvider: getGroupBuyerAccess,
  onCheckout: (handoff) => {
    beginCheckout({
      holdId: handoff.holdId,
      operationId: handoff.operationId,
    });
  },
});

await picker.render();

For example, a two-person purchase must have two seats selected on each tab. The buyer cannot hold two seats on Friday and one on Saturday.

Constructor options

Option Type Notes
container string | HTMLElement Required inline mount target.
performanceGroup string Required active pg_… key. Do not pass an Event key.
selectionMode "same_seat" | "per_performance" Defaults to same_seat. This is an offer decision made before the picker opens.
numberOfPlacesToSelect number Required and at least one for per_performance; each tab must contain this many places.
buyerAccessTokenProvider ({ reason }) => Promise<{ token, expiresAt }> Preferred group-and-origin-bound credential refresh path.
buyerAccessToken string | { token, expiresAt } Non-refreshing alternative; prefer the provider.
holdTtlMs number Requested common group hold duration; the server clamps it.
initialHoldId + initialOperationId string pair Restore only a matched group hold/operation pair. Supply both or neither.
restoreHold boolean Defaults to true; group storage is separate from single-Event holds.
locale, messages, languages, theme, colorblindSafe Presentation options Use the same accessibility and branding controls as SeatPicker.
enable3D, max3DSeats, seatView Presentation options The canonical shared chart is rendered once.
maxSelection, selectedObjects, selectableObjects, selectionValidators Selection options In per-performance mode, exact party size is still required for every date.

event, custom transport, Best Available, runtime pricing overrides, and hosted checkout are intentionally unavailable. Passing a group through SeatPicker or calling one Event’s hold route would make the purchase unsafe.

Checkout handoff

onHold and onCheckout receive this group-specific object:

PerformanceGroupCheckoutHandoff.tsts
interface PerformanceGroupCheckoutHandoff {
  operationId: string;
  holdId: string;
  expiresAt: number;
  performanceGroup: PerformanceGroupDescriptor;
  selectionMode: "same_seat" | "per_performance";
  seatLabels: string[];
  allocations?: Array<{
    eventKey: string;
    seatLabels: string[];
  }>;
}

Treat seatLabels and allocations as buyer display data only. Send the opaque holdId and operationId to your backend; it must inspect the group hold before calculating a charge or booking inventory.

The handoff deliberately never includes child Event hold IDs, authoritative prices, payment data, or ticket information.

Hold, recovery, and release

Buyer state Picker behavior Host behavior
Selecting Seats are candidates only. Do not create an order yet.
Securing seats Interaction is locked while the single group operation resolves. Keep the buyer on the picker; do not start a second hold.
Held Every performance is protected by one common expiry. Start checkout using the opaque handoff.
Conflict Nothing is presented as held after safe cleanup. Explain that availability changed and let the buyer choose again.
Recovery The picker checks the same operation after a lost response. Do not create another operation or guess the outcome.
Expired or released Selection resets and availability refreshes. Clear the in-progress checkout/cart reference.

Call picker.destroy() when the host route unmounts. The picker clears its timers, chart instance, and polling connection. It never books inventory; booking belongs to your server.

Continue with the end-to-end integration guide and group hold inspection and booking.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close