> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.todaytixgroup.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.todaytixgroup.com/_mcp/server.

# Migration: Areas to Inventory Items

Guide for moving from the Encore API's `GET /api/v4/products/{productId}/areas` and detailed availability endpoints to `GET /api/v3/events/{eventId}/occurrences/{occurrenceId}/inventory-items` in the TTG Storefront API v3, the newer API. The two APIs number their versions separately.

> For the full v4 inventory reference, see [Product Areas](https://developers.todaytixgroup.com/reference/get_api-v4-products-productid-areas) on the legacy portal.

## Endpoint Comparison

| | Legacy (v4) | New (v3) |
|---|---|---|
| **Method** | `GET` | `GET` |
| **Path** | `/api/v4/products/{productId}/areas` (+ `/detailed` availability) | `/api/v3/events/{eventId}/occurrences/{occurrenceId}/inventory-items` |
| **Auth** | None — the optional `affiliateId` header only picks the retailer | `X-TT-API-Key`, `X-TT-Retailer` and `X-TT-Platform` headers |
| **Calls per occurrence** | Two — detailed availability, then areas | One |
| **Shape** | Areas → groupings → price bands | Flat list of items, each typed |

## What Changed

### One call, not two

The legacy flow used `/detailed` availability to size up a date and time, then `/areas` to pull seats and price bands. v3 replaces both with a single call per occurrence (one date and time of an event) that returns every purchasable item — seats and general admission alike.

```
# Legacy — two calls
GET /api/v4/availability/products/123/quantity/2/from/20260315/to/20260315/detailed
GET /api/v4/products/123/areas?date=20260315&time=1930&quantity=2

# New — one call
GET /api/v3/events/123/occurrences/456/inventory-items?quantity=2
```

Legacy needed no key. v3 needs the three headers in [Authentication & Headers](/guides/getting-started#authentication--headers) on every call.

You need the occurrence `id` first — get it from `GET /api/v3/events/{eventId}/occurrences` (see [Availability → Occurrences](/migration/availability-to-occurrences)).

The legacy `quantity` carries over as the optional `?quantity=` query parameter. Seats a party of that size can't pick can still be listed: they carry `selectionBlocked`, or no `selectionGroup`, so show them as unavailable. See [Seats for a party](/guides/inventory-types#seats-for-a-party). Call again when the customer changes the ticket count.

### From areas and price bands to typed items

Legacy nested seats inside areas, and areas inside price bands, controlled by `groupBy` (`lump`, `priceBand`). v3 returns a flat list. Each item carries an `inventoryVariant` with a `_type` that tells you what it is:

| `_type` | Legacy equivalent | Quantity rule |
|---|---|---|
| `AssignedSeat` | A seat inside an area (`row`, `number`) | Always `1` |
| `GeneralAdmission` | An unreserved area / lump | `1` to `quantityAvailable`, within the event's `orderLimit` |

Switch on `_type` for rendering and quantity logic. A gift-card event's items are `GiftCard`, and new types may be added. For the full type reference, see [Inventory Types](/guides/inventory-types); skip any `_type` you don't recognize.

### Pricing: all-in `sellPrice`, not face value plus fees

Legacy split price into `faceValue`, `orderFee`, and an `includesBookingFee` flag, with a `percentageDiscount` on the side. You had to add them up. v3 gives you `sellPrice` — the all-in price the customer pays, fees and tax included. When there's a discount, `listPrice` carries the original for a strikethrough.

| Legacy (v4) | New (v3) | Notes |
|---|---|---|
| `faceValue` + `orderFee` | `sellPrice` | All-in, fees and tax included — nothing to add up |
| `percentageDiscount` | `listPrice` present | Show `listPrice` struck through next to `sellPrice` |
| `salePrice` `{ value, currency, decimalPlaces }` | `sellPrice` `{ amount, currency }` | Minor units; get the scale from the currency |
| One price per band | `priceOptions[]` (Adult, Child, Senior…) | Each item can carry several price options |

### Price bands become price options

Legacy price bands were a property of the area. v3 attaches `priceOptions[]` to each item — one entry per category (Adult, Child, Senior). Each has its own `id`, which you pass when you add the item to a cart.

### Money format

```json
// Legacy
{ "salePrice": [{ "value": 2500, "currency": "GBP", "decimalPlaces": 2 }] }

// New
{ "sellPrice": { "amount": 2500, "currency": "GBP" } }
```

Both are minor units, but v3 drops `decimalPlaces` — derive the scale from the currency code with your i18n library. See [Money Format](/guides/api-conventions#money-format).

### Seat attributes are always on

Legacy hid seat attributes behind `includeSeatsAttributes=true`. v3 always returns `attributes[]`, each tagged with a `category` (`POSITIVE`, `NEGATIVE`, `ACCESSIBILITY`, `NEUTRAL`, `FEATURED`) so you can badge them consistently. See [Inventory Types](/guides/inventory-types#inventory-attributes).

## Response Shape

### New (v3)

```json
{
  "data": [
    {
      "id": "v1_eyJwcm92aWRlcklkIjoxMDAwLCJzZWF0SWQiOiJPUkNIRVNUUkEtQS0xMDEifQ",
      "inventoryVariant": {
        "_type": "AssignedSeat",
        "id": "ORCHESTRA-A-101",
        "name": "Orchestra A 101",
        "section": "Orchestra",
        "row": "A",
        "seat": "101",
        "attributes": []
      },
      "quantityAvailable": 1,
      "priceOptions": [
        {
          "id": "eyJwcm92aWRlckNvZGUiOiJGVUxMUFJJQ0UiLCJob2xkQ29kZSI6IjE1NzM5In0",
          "category": { "name": "Adult" },
          "listPrice": { "amount": 15000, "currency": "USD" },
          "sellPrice": { "amount": 12500, "currency": "USD" }
        }
      ]
    }
  ],
  "pagination": { "limit": 50, "offset": 0, "total": 1 }
}
```

Key differences:
- The response is paginated — items are in `data[]`, with a `pagination` block. See [Pagination](/guides/api-conventions#pagination).
- The item `id` is a versioned, opaque token (`v1_…`) — pass it whole, with a `priceOptionId`, to `POST /api/v3/carts`. `priceOptionId` has no prefix. Never build either by hand.
- Inventory item and price option IDs are tokens — the same seat, price and rate code give the same token. Still re-fetch inventory before you build a cart: prices and price options can change.
- `inventoryVariant.id` is **stable** across occurrences — the same seat returns the same variant ID on every date, safe to cache for seating charts

## Migration Checklist

1. Send `X-TT-API-Key`, `X-TT-Retailer` and `X-TT-Platform` on every call
2. Replace the two-call area/detailed flow with one `inventory-items` call per occurrence
3. Get the occurrence `id` first from `GET /api/v3/events/{eventId}/occurrences`
4. Switch on `inventoryVariant._type` instead of walking areas and price bands
5. Read `sellPrice` as the all-in price — stop summing `faceValue` and `orderFee`
6. Show `listPrice` struck through when it's present; there's no `percentageDiscount` to apply
7. Read each item's `priceOptions[]` for its prices; keep the `priceOptionId` for the cart step
8. Drop `decimalPlaces` — derive the scale from the currency code
9. Treat the item `id` (the whole `v1_…` token) and `priceOptionId` as opaque; re-fetch inventory before each cart, because prices can change
10. Page with `limit` / `offset` until `offset >= pagination.total`

## Next Steps

With inventory in hand, continue the [Purchase Flow](/guides/purchase-flow):

1. **Create Cart** — `POST /api/v3/carts` (see [Baskets → Carts](/migration/baskets-to-carts))
2. **Checkout** — `POST /api/v3/orders` (see [Checkout → Orders](/migration/checkout-to-orders))