Skip to content

Season picker

Let a buyer choose one seat package for every performance in a fixed renewable Season, then hand the inventory hold to your checkout.

Updated View as Markdown

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.

Use this page for picker configuration and behavior. Follow the end-to-end Season integration for Plan publication, rehearsal, checkout, booking, rights, and renewal, or the Seasons API for exact trusted operations.

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:

npm install @seatlayer/js
browser/season-picker.tsts
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.

Best Available for a Season

holdBestAvailable(qty, operationId, options?) asks SeatLayer for the best qty seats that are free on every performance in the Plan, then holds them exactly as a map selection would. It resolves to the same SeasonCheckoutHandoff that onHold receives.

browser/season-best-available.tsts
const handoff = await picker.holdBestAvailable(2, `sop_${crypto.randomUUID()}`, {
  categoryKey: "stalls",        // optional
  zoneId: "zone_lower",         // optional
  accessibility: ["wheelchair"], // optional, any-of across every Event
  requireContiguous: true,      // refuse scattered seats when no block of 2 exists
});
sessionStorage.setItem("seasonOperationId", handoff.operationId);
  • operationId must start with sop_ and stay the same on a retry, so a lost response recovers the same hold instead of creating a second one.
  • qty is 1 to 10 seats per call, and never more than maxSelection.
  • One call runs at a time. A second call while the first is pending fails with season_best_available_in_progress.
  • The server route is POST /pub/seasons/:key/best-available, using the same Season buyer session as the picker.
  • In 0.106.1 the React SeasonPickerHandle and the Vue and Angular components expose holdSameSeat() but not holdBestAvailable(). Use @seatlayer/js directly for this call.

React

npm install @seatlayer/react
components/SeasonSeats.tsxtsx
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.

Vue

npm install @seatlayer/vue
components/SeasonSeats.vuevue
<script setup lang="ts">
import { SeasonPicker } from "@seatlayer/vue";
</script>

<template>
  <SeasonPicker
    season="sea_main_stage_2027"
    :buyer-access-token="session.token"
    @continue="handoff => beginCheckout(handoff.operationId)"
  />
</template>

Angular

npm install @seatlayer/angular

Import SeatLayerSeasonPickerComponent into a standalone component or NgModule, then render its seatlayer-season-picker selector:

season-seats.component.htmlhtml
<seatlayer-season-picker
  #seasonPicker
  season="sea_main_stage_2027"
  [buyerAccessToken]="session.token"
  (continued)="beginCheckout($event.operationId)"
/>

All four browser/framework packages use the same protected Season protocol and return the same opaque, price-free handoff.

Checkout boundary

Treat the browser handoff as an inventory reference, not proof of purchase:

  1. Mint the bss_… session on your trusted server for the exact Season, published Plan, environment, and browser origin.
  2. Let SeasonPicker create one all-or-nothing hold for the selected labels across every included Event.
  3. Inspect that operation from your server and calculate the authoritative package price and benefits. The inspection is identity-only: it includes Event, seat, category, optional tier, and quantity, never an amount.
  4. Take payment in your platform.
  5. Book the same operation with a caller-stable action ID and your bookingRef.
  6. 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.
  • operationId and release/book action IDs are caller-stable recovery keys.
  • onHold means inventory is temporarily held. onContinue means the buyer pressed the explicit checkout action. Neither means payment or booking.
  • The buyer picks exact seats on the map. To let SeatLayer choose, call holdBestAvailable() from @seatlayer/js (see below).
  • 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.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close