SeasonPicker is the buyer seating journey for a fixed renewable Season of
2–20 performances that share one published seating chart. The buyer chooses
once, reviews the full package, and keeps those exact seats for every included
performance.
This is different from a Performance Group: a Season has an immutable published Plan, durable Seat Rights for incumbents, and renewal offers. A buyer cannot remove dates or choose a different seat for each performance.
| Product | Best suited to | What the buyer chooses |
|---|---|---|
| Single Event | One show, match, or screening | Seats for that Event |
| Performance Group | A short run or flexible multi-date bundle | Same seats across a run, or an equal party size per date |
| Season | Theatre, sports, memberships, and subscriptions with future renewal | One seat package for the complete published Plan |
JavaScript
Mint a short-lived Season buyer session on your server, then render the picker:
import { SeasonPicker } from "@seatlayer/js";
const picker = new SeasonPicker({
container: "#season-picker",
season: "sea_main_stage_2027",
buyerAccessTokenProvider: async () => {
const response = await fetch("/api/seatlayer/season-access", {
method: "POST",
credentials: "same-origin",
cache: "no-store",
});
if (!response.ok) throw new Error("Unable to open Season seat selection");
return response.json(); // { token: "bss_…", expiresAt }
},
offer: {
eyebrow: "2027 membership",
priceLabel: "From $480",
compareAtPriceLabel: "From $600 bought separately",
savingsLabel: "Save $120",
priceNote: "Final package price depends on your seat and is confirmed at checkout",
benefits: ["Priority entry", "Free ticket exchange", "Renewal priority"],
renewalLabel: "Same-seat renewal eligible",
},
onHold: (handoff) => {
// Persist operationId so the same hold can be recovered after a lost response.
sessionStorage.setItem("seasonOperationId", handoff.operationId);
},
onContinue: (handoff) => {
beginCheckout({
season: handoff.seasonKey,
holdId: handoff.holdId,
operationId: handoff.operationId,
planActivationId: handoff.planActivationId,
});
},
onError: (error) => reportSeatSelectionError(error),
});
await picker.render();The picker shows the Season promise, venue, complete performance list, benefits,
aggregate availability, interactive chart, selection summary, and a clear
choose → review → checkout progression. Package copy in offer is display-only;
calculate and verify the authoritative amount again on your server.
Event-level chart amounts, ticket tiers, section price ranges, and totals are
suppressed in Season mode so they cannot be mistaken for the package price. If
your Season price varies by seat category, present a truthful starting price or
update your surrounding storefront from onSelectionChange; never multiply an
Event amount in the browser.
React
import { SeasonPicker } from "@seatlayer/react";
export function SeasonSeats() {
return (
<SeasonPicker
season="sea_main_stage_2027"
buyerAccessTokenProvider={getSeasonBuyerAccess}
offer={{
priceLabel: "From $480",
savingsLabel: "Save $120",
benefits: ["Priority entry", "Renewal priority"],
}}
maxSelection={4}
onContinue={(handoff) => beginCheckout(handoff)}
/>
);
}The React component mounts and destroys the canonical JavaScript picker. It does not duplicate Season inventory logic.
Checkout boundary
Treat the browser handoff as an inventory reference, not proof of purchase:
- Mint the
bss_…session on your trusted server for the exact Season, published Plan, environment, and browser origin. - Let
SeasonPickercreate one all-or-nothing hold for the selected labels across every included Event. - Inspect that operation from your server and calculate the authoritative package price and benefits.
- Take payment in your platform.
- Book the same operation with a caller-stable action ID and your
bookingRef. - Issue the pass or tickets only after booking reaches a terminal success.
See the Seasons server API for mint, inspect, book, cancellation, renewal, and recovery operations.
Important behavior
- Availability is the intersection across the complete published Plan. A seat unavailable on one performance is unavailable for the Season.
- Holds are atomic: every allocation succeeds or none is presented as held.
operationIdand release/book action IDs are caller-stable recovery keys.onHoldmeans inventory is temporarily held.onContinuemeans the buyer pressed the explicit checkout action. Neither means payment or booking.- Best available is intentionally unsupported for v1; the buyer chooses exact seats and can review the package before checkout.
createRenewalIntent(offerId)records intent only. Your platform confirms renewal price, payment, order, and fulfilment.
Core options
| Option | Type | Notes |
|---|---|---|
container |
string | HTMLElement |
Required JavaScript mount target. |
season |
string |
Required sea_… key with a published Plan. |
buyerAccessTokenProvider |
() => Promise<{ token, expiresAt }> |
Preferred origin-bound credential refresh path. |
buyerAccessToken |
string | { token, expiresAt } |
Non-refreshing alternative. |
offer |
SeasonOfferPresentation |
Display-only package price, savings, benefits, and renewal copy. |
maxSelection |
number |
Maximum seats in one Season package hold. |
initialOperationId |
string |
Restores the same accepted or pending hold after interruption. |
initialView / enable3D / theme |
picker options | Reuses the standard chart presentation controls. |
onHold / onHoldChange |
callbacks | Observe the opaque inventory handoff. |
onContinue |
callback | Start host checkout after the buyer reviews the held package. |
onStatusChange / onError |
callbacks | Present operational and failure states in the host application. |