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.
Tier model
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
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
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
Buyer chooses
Selection state carries the tier id and buyer-facing price.
SDK holds
SeatLayer validates the tier against the selected category and freezes the line item.
Browser hands off
Send the opaque holdId to your server. Do not send tier or price as the
authority.
Server inspects
GET /v1/events/:eventKey/holds/:holdId returns tierId, unitPrice,
currency, and quantity for each authoritative item.
Server books
Charge from inspected items and book using holdId and bookingRef.
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 or complete checkout.