Migration: Baskets to Carts

Guide for migrating from the legacy PATCH /api/v1/baskets flow to the new POST /api/v3/carts flow.

For the full v1 basket reference, see Baskets on the legacy portal.

Endpoint Comparison

Legacy (v1)New (v3)
Create / updatePATCH /api/v1/basketsPOST /api/v3/carts
ReadGET /api/v1/baskets/{reference}GET /api/v3/carts/{cartId}
Add / change itemsRe-send the whole basketPOST /api/v3/carts/{cartId}/items (extras only)
Apply a discountPATCH /api/v1/baskets/{reference}/applyPromotionPOST /api/v3/carts/{cartId}/vouchers
Remove an itemDELETE /api/v1/baskets/{reference}/reservations/{reservationId}Create a new cart
Empty the basketPATCH /api/v1/baskets/{reference}/clearDELETE /api/v3/carts/{cartId}
AuthchannelId + checksumX-TT-API-Key header

What Changed

The server owns the cart ID

In v1 you supplied a unique reference and a checksum password, then PATCHed the same basket to build it up. In v3 you POST the items you want and the server returns a cartId. There’s no reference to invent and no checksum to manage — the API key authenticates you.

# Legacy — you pick the reference, PATCH to upsert
PATCH /api/v1/baskets
{ "reference": "1010101", "channelId": "...", "checksum": "...", "reservations": [ ... ] }
# New — POST items, server returns the cart
POST /api/v3/carts
{ "items": [ ... ] }

From reservations to items

Legacy grouped tickets into reservations, each with a productId, a quantity, and opaque aggregateReference item tokens that bundled the price inside them. v3 takes a flat items list. Each item names the occurrence, the inventory item, and the price option separately.

1{
2 "items": [
3 {
4 "occurrenceId": "123",
5 "inventoryItemId": "v1_eyJzIjoiT1JDSC1BLTEwMSJ9",
6 "priceOptionId": "eyJjIjoiRlVMTCJ9",
7 "quantity": 1
8 }
9 ]
10}
Legacy (v1)New (v3)Notes
reservations[].productIditems[].occurrenceIdv3 reserves against a specific occurrence, not a product
aggregateReference (price baked in)inventoryItemId + priceOptionIdPrice is chosen by option, not encoded in the token
reservations[].quantityitems[].quantityPer item; assigned seats are always 1
channelId / checksum(API key)Auth moves to the key

Get inventoryItemId and priceOptionId from the inventory step (see Areas → Inventory Items). All items must belong to the same occurrence.

The hold timer is explicit

Legacy baskets held inventory, but the expiry wasn’t part of the response. v3 returns expiresAt (UTC) on the cart — show a countdown. When it passes, the hold is released and GET /api/v3/carts/{cartId} returns 404. Create a new cart to continue. To release a hold early, DELETE /api/v3/carts/{cartId} (idempotent, returns 204).

Discounts move to a voucher endpoint

Legacy applied a coupon inline on the basket or through applyPromotion. v3 has a dedicated pair:

POST /api/v3/carts/{cartId}/vouchers { "code": "SAVE20" }
DELETE /api/v3/carts/{cartId}/vouchers/{code}

The discount shows up in the cart’s receiptLines[] and total.

Changing seats means a new cart

Legacy let you drop a single reservation with DELETE .../reservations/{reservationId}. In v3 a cart’s seats are fixed once created — to change them, create a new cart. Only extras can be added to a live cart, with POST /api/v3/carts/{cartId}/items (see Purchase Flow).

Money format

1// Legacy basket money
2{ "value": 8500, "currency": "USD", "decimalPlaces": 2 }
3
4// New cart total
5{ "amount": 8500, "currency": "USD" }

Both are minor units; v3 drops decimalPlaces — derive the scale from the currency. A cart owns its currency: it’s set at creation (base, or the body’s currency when supported) and switched with PATCH /api/v3/carts/{cartId} { "currency": "EUR" }. See Currency.

Totals come from the server

Don’t compute the total yourself. Read total and render receiptLines[] in order — each line has a sign (positive = charge, negative = credit). You’ll pass total straight through as expectedTotal at checkout.

Response Shape

New (v3)

1{
2 "data": {
3 "id": "6aac14d3-5826-4da8-98b6-9e68c28629d6",
4 "expiresAt": "2026-03-15T19:40:00Z",
5 "total": { "amount": 18500, "currency": "USD" },
6 "items": [ ... ],
7 "receiptLines": [ ... ],
8 "options": {
9 "currencies": ["USD", "EUR"],
10 "extras": [ ... ]
11 }
12 }
13}

Key differences:

  • id is the server-generated cart ID — stable, safe to store (see IDs)
  • expiresAt is the hold deadline in UTC — drive a countdown from it
  • options.extras lists add-ons you can attach; the key is absent when the event offers none
  • options.currencies lists the currencies the cart can be priced in

Migration Checklist

  1. Replace PATCH /api/v1/baskets with POST /api/v3/carts; let the server return the cartId
  2. Drop the self-assigned reference and checksum; authenticate with the API key
  3. Convert reservations to a flat items list keyed by occurrenceId, inventoryItemId, and priceOptionId
  4. Keep all items in one cart to a single occurrence
  5. Read expiresAt and show a countdown; handle the 404 on expiry by creating a new cart
  6. Move promo codes to POST /api/v3/carts/{cartId}/vouchers
  7. Replace single-reservation removal with “create a new cart”; use DELETE to release a hold
  8. Read total and receiptLines[] from the server — don’t recompute; drop decimalPlaces

Next Steps

With a cart held, continue the Purchase Flow:

  1. Add Extras (optional)POST /api/v3/carts/{cartId}/items
  2. CheckoutPOST /api/v3/orders (see Checkout → Orders)