---
title: "Groups and general admission"
description: "Hold GA quantities and find adjacent reserved seats without changing the server booking flow."
---

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

# Groups and general admission

General-admission areas and reserved-seat groups use different selection UX, but
they produce the same server boundary: an authoritative hold that your backend
inspects and books atomically.

> **SeatPicker includes both flows**
>
> `SeatPicker` renders GA quantity controls and best-available group selection
> in its buyer UI. Use the headless `SeatingChart` methods below when your
> application owns those controls.

## General-admission inventory

A `gaArea` is a chart object with capacity instead of individually presented
seats. A chart can mix reserved rows, tables, booths, and GA areas in one event.

With `SeatingChart`, respond to `onGAClick` and hold the buyer's requested
quantity:

```js title="browser/ga-selection.js"
const chart = new seatlayer.SeatingChart({
  container: "#chart",
  event: "ev_9f3a",
  onGAClick: async (area) => {
    const quantity = await askForQuantity({
      label: area.label,
      remaining: area.available,
    });

    if (quantity > 0) {
      await chart.holdGA(area.id, quantity);
    }
  },
  onHold: ({ holdId, expiresAt, items }) => {
    saveCheckoutHandoff({ holdId, expiresAt, items });
  },
});

await chart.render();
```

SeatLayer materializes internal GA units for inventory correctness. Treat their
labels as opaque. For buyer-facing totals, aggregate hold items by `objectId` and
sum `quantity ?? 1`.

```js title="server/group-ga-lines.js"
function summarizeHold(items) {
  return Object.values(
    items.reduce((groups, item) => {
      const key = `${item.objectId}:${item.tierId ?? "base"}`;
      groups[key] ??= {
        objectId: item.objectId,
        categoryKey: item.categoryKey,
        tierId: item.tierId,
        unitPrice: item.unitPrice,
        currency: item.currency,
        quantity: 0,
      };
      groups[key].quantity += item.quantity ?? 1;
      return groups;
    }, {}),
  );
}
```

Do not generate GA unit labels in your application or accept them from the buyer
as trusted booking input.

## Adjacent reserved seats

`bestAvailable(quantity, categoryKey?, options?)` asks SeatLayer to find and
hold a suitable adjacent group in one operation.

```js title="browser/best-available.js"
const result = await chart.bestAvailable(4);

if (!result) {
  showNoGroupMessage("We could not find four seats together.");
} else {
  console.log(result.holdId, result.labels);
}
```

Restrict the search to a category:

```js
const result = await chart.bestAvailable(2, "vip");
```

A successful result updates the chart selection and triggers `onHold` like a
manual hold. Continue through the same server inspection, payment, and booking
flow.

## Use intentional fallback rules

1. **Ask for the desired group**

   Start with the buyer's quantity and optional category or zone preference.
2. **Explain an exact failure**

   If no adjacent block exists, say that clearly; do not report the whole event
   as sold out when scattered seats may remain.
3. **Offer a buyer-controlled alternative**

   Let the buyer reduce quantity, choose a different category, or switch to
   manual selection. Do not silently split the group.
4. **Book the hold atomically**

   Send only its `holdId` to your server. A conflict fails the whole booking, so
   a four-person order cannot accidentally confirm three seats.

## Server booking is unchanged

```js title="server/book-group.js"
const response = await fetch(
  `https://api.seatlayer.io/v1/events/${eventKey}/book`,
  {
    method: "POST",
    headers: {
      authorization: `Bearer ${process.env.SEATLAYER_SECRET_KEY}`,
      "content-type": "application/json",
    },
    body: JSON.stringify({
      holdId,
      bookingRef: order.id,
    }),
  },
);

if (response.status === 409) {
  return sendBuyerBackToGroupSelection();
}
```

Whether the hold contains clicked seats, an adjacent group, a GA quantity, or a
mixture, booking is all-or-nothing.

## Accessibility and product details

- Label GA controls with the area name, remaining quantity, and chosen count.
- Provide increment/decrement buttons with keyboard and screen-reader labels.
- Announce the best-available result and hold expiry without moving focus
  unexpectedly.
- Preserve accessible-seat metadata when offering group alternatives.
- Show the group as one order line when that matches buyer expectations, while
  retaining item-level data in the order record.
- Never use colour alone to distinguish available, held, and booked inventory.

## Verification checklist

- [ ] GA capacity is configured on the chart object.
- [ ] Quantity validation prevents zero, negative, and over-capacity requests.
- [ ] Buyer totals come from inspected `unitPrice`, currency, and quantity.
- [ ] Best-available failure does not silently split the group.
- [ ] Manual fallback preserves the requested category/accessibility needs.
- [ ] A mixed reserved + GA hold completes in test mode.
- [ ] Expiry and `409` return the entire order to a useful state.
- [ ] Keyboard and screen-reader behavior is tested for quantity controls.

Continue with [best available](/buyer-sdk/best-available),
[ticket tiers](/buyer-sdk/ticket-tiers), or
[integrate your backend](/integrations/ticketing-backend).

Source: https://docs.seatlayer.io/integrations/groups-and-ga/index.mdx
