---
title: "Integrate a Fixed Renewable Season"
description: "Build a fixed same-seat Season from compatible Events through Plan publication, buyer selection, trusted checkout, Seat Rights, and renewal."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.seatlayer.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Integrate a Fixed Renewable Season

Use a Fixed Renewable Season when one buyer should receive the same assigned
seats across an immutable programme of 2–20 Events and may keep a protected
path to those seats in a successor Plan.

This page owns the **end-to-end integration**. Use the
[Season picker](/buyer-sdk/seasons/) for buyer UI options and the
[Seasons API](/server-api/seasons/) for exact operations, request fields,
responses, lifecycle recovery, and support tooling.

> **SeatLayer coordinates inventory; your platform owns commerce**
>
> SeatLayer owns the Plan membership, same-seat availability, holds, bookings,
> Contracts, Seat Rights, and renewal inventory. Your trusted system owns
> membership eligibility, package pricing, discounts, tax, payment, commercial
> orders, passes or tickets, refunds, benefits, and customer support.

> **Public Season surfaces**
>
> The released Season contract is available through browser SDKs, REST, and all
> seven server SDKs. Native SDKs and Season Best Available are not supported.
> The production organizer CMS remains rollout-controlled; do not plan a
> self-serve CMS workflow without explicit SeatLayer confirmation.

## Confirm that Season is the right model

| Requirement | Use |
|---|---|
| One dated occurrence | [Event flow](/start/how-it-works/) |
| One atomic purchase across a fixed 2–8 Event run, with no retained renewal right | [Performance Group](/integrations/performance-groups/) |
| Same assigned seats across an immutable 2–20 Event Plan, with durable rights and renewal | **Fixed Renewable Season** |

A Season is not a marketing name for several events. It adds four inventory
concepts that a Performance Group deliberately does not have:

- a **Plan** fixes the complete Event membership and publication identity;
- a **Contract** records the holder's relationship to that Plan;
- a **Seat Right** protects the holder's assigned seats; and
- a **Renewal Offer** coordinates those rights into a successor Plan.

Read the full [inventory model comparison](/start/inventory-models/) before
provisioning either multi-event product.

## The complete Season journey

## 1. Prepare compatible Events

Create one Event for every dated occurrence. Before creating the Season,
confirm that all Events share:

- the same organization and workspace;
- the same test or live mode;
- the same published Chart snapshot and assigned-seat inventory model;
- the same venue, timezone, and currency;
- a start time; and
- Platform/integrator checkout ownership.

The currency is a compatibility guard. It does not make Event prices the
Season package price. Your commerce system calculates the package amount.

Season v1 does not inherit private Sales Channel allocation. Use ordinary
public Event inventory for the Plan; treat
[Sales Channels](/platform/sales-channels/) as a separate per-Event access
model, not as a Season membership system.

## 2. Validate, create, activate, and publish

Preflight the intended Event set before a write. Validation reports the exact
incompatible Event and field so an operator can correct the source rather than
discovering the mismatch during publication.

```http title="Validate compatible Season inventory"
POST /v1/seasons/validate HTTP/1.1
Authorization: Bearer sk_test_••••••••
Content-Type: application/json

{
  "eventKeys": ["ev_opening", "ev_matinee", "ev_closing"]
}
```

Create the draft with a retry-safe idempotency key:

```http title="Create a Season draft"
POST /v1/seasons HTTP/1.1
Authorization: Bearer sk_test_••••••••
Idempotency-Key: membership-2027-draft
Content-Type: application/json

{
  "name": "Main stage membership",
  "edition": "2027",
  "eventKeys": ["ev_opening", "ev_matinee", "ev_closing"]
}
```

The draft contains a Plan. Activate the Season structure, then publish that
exact Plan revision. These are separate transitions because structural review
and buyer-facing publication are separate operational decisions.

```http title="Activate the Season structure"
POST /v1/seasons/sea_main_stage/activate HTTP/1.1
Authorization: Bearer sk_test_••••••••
Content-Type: application/json

{ "expectedRevision": 1 }
```

```http title="Publish the immutable Plan"
POST /v1/seasons/sea_main_stage/plans/spl_2027/publish HTTP/1.1
Authorization: Bearer sk_test_••••••••
Content-Type: application/json

{ "expectedRevision": 2 }
```

Lifecycle mutations may return `202 Accepted`. Follow the returned `Location`,
honour `Retry-After`, and retain the original operation identity until it is
terminal. Do not create a replacement Season because one response was lost.

Activation does not publish a Plan, and publication does not open sales.

## 3. Rehearse before opening sales

The sales gate proves that the inventory and recovery path works as one system.
In test mode, complete one coherent journey through the real public surfaces:

1. mint a Season buyer session for the test origin;
2. create a same-seat hold with `SeasonPicker`;
3. inspect and book that hold from the trusted backend;
4. cancel the test booking with an explicit Seat Right disposition; and
5. receive successful webhook delivery for the required lifecycle events.

Then ask SeatLayer to validate the retained evidence:

```http title="Validate buyer rehearsal"
POST /v1/seasons/sea_main_stage/buyer-rehearsals/validate HTTP/1.1
Authorization: Bearer sk_test_••••••••
```

Once rehearsal succeeds, open sales against the revision you reviewed:

```http title="Open Season sales"
POST /v1/seasons/sea_main_stage/sales/open HTTP/1.1
Authorization: Bearer sk_test_••••••••
Content-Type: application/json

{ "expectedRevision": 3 }
```

If the rehearsal is incomplete, fix the failed hold, booking, cancellation, or
webhook-delivery boundary. Do not bypass the gate or paste operation ids into an
operator screen.

## 4. Mint buyer access and render the picker

Authenticate and authorize the buyer in your application first. Your backend
then mints one short-lived, exact-origin Season session and returns only the
token and expiry to the browser.

```http title="Create Season buyer access"
POST /v1/seasons/sea_main_stage/buyer-access-sessions HTTP/1.1
Authorization: Bearer sk_live_••••••••
Content-Type: application/json

{
  "allowedOrigin": "https://tickets.example.com",
  "includePublic": true,
  "maxQuantity": 4,
  "buyerRef": "member_8372"
}
```

Keep the secret key on the server and the returned `bss_…` token in browser
memory. Never place either credential in a URL, analytics property, exception
message, or persistent browser storage.

Render `SeasonPicker` with a token provider. The picker shows the complete Plan,
finds the availability intersection across every included Event, and creates
one all-or-nothing same-seat hold.

```ts title="browser/season-selection.ts"
import { SeasonPicker } from "@seatlayer/js";

const picker = new SeasonPicker({
  container: "#season-picker",
  season: "sea_main_stage",
  buyerAccessTokenProvider: () =>
    fetch("/api/seatlayer/season-access", {
      method: "POST",
      credentials: "same-origin",
      cache: "no-store",
    }).then((response) => response.json()),
  onContinue: (handoff) => {
    startCheckout({
      seasonKey: handoff.seasonKey,
      operationId: handoff.operationId,
      holdId: handoff.holdId,
    });
  },
});

await picker.render();
```

Use the picker's `offer` content only to explain the package. It is display
copy, not trusted pricing. Do not multiply an Event price in the browser and
call the result a Season total.

## 5. Inspect, price, pay, and book

Send the opaque operation identity to your backend. Inspect the retained hold
there and verify that it is still committed to the expected Season and Plan.
The inspection returns trusted Event and seat allocations but deliberately no
authoritative commercial total.

Your backend should then:

1. calculate the package amount, benefits, tax, and discounts from its own
   catalogue;
2. create or recover one commercial order;
3. authorize or capture payment according to its recovery policy; and
4. book the same Season operation with one caller-stable action id and the
   order's immutable `bookingRef`.

```http title="Book the Season hold"
POST /v1/seasons/sea_main_stage/holds/sop_01/book HTTP/1.1
Authorization: Bearer sk_live_••••••••
Content-Type: application/json

{
  "bookActionId": "season-book:order_1042",
  "bookingRef": "order_1042"
}
```

If booking returns `202 Accepted`, poll the returned booking location. Reuse
the same `bookActionId` and `bookingRef` after a lost response. Never issue the
pass or per-occurrence tickets until the booking is terminally successful.

## 6. Store the commerce-to-inventory links

After booking, retain these relationships in your own order system:

| Host record | SeatLayer identity | Why retain it |
|---|---|---|
| Commercial order | `bookingRef` and booking action | Retry, support, and reconciliation |
| Member or account | `holderRef` / buyer reference | Find the correct Contract without treating it as payment identity |
| Membership edition | Season key and published Plan identity | Explain exactly which occurrence set was sold |
| Assigned package | Contract and Seat Right identities | Renewal, cancellation, and support |
| Fulfilment | Child Event allocations | Issue the correct passes or tickets for every occurrence |

SeatLayer's Contract and Seat Rights prove inventory entitlement. Your order
still proves the amount charged, tax, payment status, refund state, benefits,
and fulfilment.

## 7. Renew, cancel, and operate deliberately

For an incumbent programme, import holders only through the Season import
workflow and keep your own stable holder reference. For renewal:

1. create and publish the successor Plan;
2. generate time-bounded Renewal Offers for eligible Contracts;
3. present the commercial renewal terms in your product;
4. inspect the accepted renewal inventory on your server;
5. take payment; and
6. commit the offer with a stable action and order reference.

A buyer renewal intent is not a completed renewal and does not authorize a
charge. Treat it as the start of your commerce workflow.

Cancellation requires an explicit Seat Right disposition:

- **preserve** keeps the future right while cancelling the current inventory
  booking according to your policy;
- **release** returns the right as well as the booked allocation.

Neither choice creates a refund. Payment compensation remains in your system.
Use Season operations, reports, support lookups, and the retained outbox to
reconcile uncertain or partially terminal outcomes; do not retry with new
action ids or attempt independent child-Event rollback.

## Go-live checklist

- [ ] The Event set is final, compatible, and uses one published Chart snapshot.
- [ ] The buyer uses exact seat choice on the released browser picker; the plan
      does not depend on native Season or Best Available support.
- [ ] Product owners chose Season instead of a single Event or Performance Group
      for a documented reason.
- [ ] Package price, benefits, tax, payment, refund, pass, and support ownership
      are implemented outside the inventory handoff.
- [ ] Test rehearsal proves hold, inspect, book, cancel, and webhook delivery.
- [ ] Browser credentials are exact-origin, short lived, kept in memory, and
      never carry a secret key.
- [ ] Lost responses reuse the same lifecycle and booking operation identities.
- [ ] The commercial order stores its Season, Plan, booking, Contract, and Seat
      Right relationships.
- [ ] Fulfilment covers every included Event and begins only after booking
      succeeds.
- [ ] Cancellation has an explicit payment policy and Seat Right disposition.
- [ ] Renewal terms and deadlines are owned by the host commerce system and
      tested against a successor Plan.

## Continue by surface

| If you are implementing… | Continue with |
|---|---|
| Buyer selection and UI callbacks | [Season picker](/buyer-sdk/seasons/) |
| Catalogue, lifecycle, booking, import, renewal, and support operations | [Seasons API](/server-api/seasons/) |
| A shorter multi-performance purchase without renewal rights | [Performance Group integration](/integrations/performance-groups/) |
| A mobile Event picker | [Native app SDKs](/buyer-sdk/mobile/) |

Source: https://docs.seatlayer.io/integrations/seasons/index.mdx
