---
title: "Ticket tiers"
description: "Carry Adult, Child, Senior, and other per-seat price choices from the chart into a trusted checkout."
---

> 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.

# Ticket tiers

A category can offer multiple ticket tiers for the same inventory—for example
Adult, Child, and Senior. The buyer chooses a seat and a tier; SeatLayer freezes
that combination into the hold.

> **SeatPicker includes the tier UI**
>
> `SeatPicker` renders tier controls in its tray and includes the selected tier
> in `CheckoutHandoff.lineItems`. Use the methods below with `SeatingChart` when
> your application owns the cart UI.

## Tier model

```ts
interface CategoryTier {
  id: string;
  name: string;
  price: number;
}
```

Tiers are authored on a category. A category without tiers keeps its single base
price.

## Read selected-seat tiers

```js title="browser/tier-controls.js"
const chart = new seatlayer.SeatingChart({
  container: "#chart",
  event: "ev_9f3a",
  onSelectionChange: (seats) => {
    for (const seat of seats) {
      if (!seat.tiers?.length) continue;

      renderTierSelect({
        seatId: seat.id,
        options: seat.tiers,
        selected: seat.tierId,
        onChange: (tierId) => chart.setSeatTier(seat.id, tierId),
      });
    }
  },
});

await chart.render();
```

The selected seat's `price` updates when `tierId` changes. Use that value for
immediate buyer display, but use hold inspection on your server for payment.

## Change or reset a tier

```js
chart.setSeatTier("seat-42", "child");

// Revert to the category's default/first tier.
chart.setSeatTier("seat-42", null);
```

An invalid tier/seat combination leaves selection unchanged.

## Carry the tier through checkout

1. **Buyer chooses**

   Selection state carries the tier id and buyer-facing price.
2. **SDK holds**

   SeatLayer validates the tier against the selected category and freezes the
   line item.
3. **Browser hands off**

   Send the opaque `holdId` to your server. Do not send tier or price as the
   authority.
4. **Server inspects**

   `GET /v1/events/:eventKey/holds/:holdId` returns `tierId`, `unitPrice`,
   `currency`, and `quantity` for each authoritative item.
5. **Server books**

   Charge from inspected items and book using `holdId` and `bookingRef`.

```js title="server/tier-total.js"
const hold = await inspectSeatLayerHold(eventKey, holdId);

const total = hold.items.reduce(
  (sum, item) => sum + item.unitPrice * (item.quantity ?? 1),
  0,
);

await createPayment({
  currency: hold.items[0]?.currency,
  amountMajor: total,
  lines: hold.items.map((item) => ({
    label: item.label,
    tierId: item.tierId,
    unitPrice: item.unitPrice,
    quantity: item.quantity ?? 1,
  })),
});
```

If a buyer changes a tier after a hold exists, create the updated hold before
continuing checkout so authoritative items match the choice. Clear any stale
server cart data when the active hold changes.

## UX and policy guidance

- Use buyer-facing tier names, but store stable tier ids on order lines.
- Explain eligibility requirements for discounted tiers in your own checkout.
- Do not infer age or accessibility eligibility from the seat itself.
- Keep tier selection keyboard accessible and associate it with the seat label.
- Recalculate displayed totals immediately, then verify against server inspection.
- If a tier disappears after a chart/event update, return the buyer to a clear
  choice instead of silently substituting another tier.

## Verification checklist

- [ ] Every tier has a stable id, readable name, and intended price.
- [ ] Default tier behavior is explicit.
- [ ] Selection updates when `setSeatTier()` succeeds.
- [ ] The browser forwards only `holdId`.
- [ ] Server totals use `unitPrice`, currency, and quantity from inspection.
- [ ] Tier changes replace stale hold/cart state.
- [ ] Eligibility and accessibility copy belongs to the host checkout.

Continue with [holds and checkout](/buyer-sdk/holds-and-checkout) or
[complete checkout](/examples/complete-checkout).

Source: https://docs.seatlayer.io/buyer-sdk/ticket-tiers/index.mdx
