Skip to navigation

Migration: Baskets to Carts

Guide for moving from the Encore API’s PATCH /api/v1/baskets flow to the POST /api/v3/carts flow in the TTG Storefront API v3, the newer API. The two APIs number their versions separately.

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 itemsRe-send the whole basketPOST /api/v3/carts/{cartId}/items (extras only, one or more per request)
Change an item’s quantityRe-send the whole basketPATCH /api/v3/carts/{cartId}/items/{itemId} (extras only)
Apply a discountPATCH /api/v1/baskets/{reference}/applyPromotionPOST /api/v3/carts/{cartId}/vouchers
Remove an itemDELETE /api/v1/baskets/{reference}/reservations/{reservationId}DELETE /api/v3/carts/{cartId}/items/{itemId} (extras only); a new cart to change seats
Empty the basketPATCH /api/v1/baskets/{reference}/clearDELETE /api/v3/carts/{cartId}
AuthNone — channelId in the body only picks the retailerX-TT-API-Key, X-TT-Retailer and X-TT-Platform headers

What Changed

Create with POST, not upsert with PATCH

Both APIs make the ID on the server. In v1 you sent PATCH /api/v1/baskets without a reference to create a basket, then sent the returned reference back to change it. In v3 you POST the items you want and the server returns a cart id.

# Legacy — PATCH with no reference creates; channelId picks the retailer
PATCH /api/v1/baskets
{ "channelId": "...", "reservations": [ ... ] }
# New — POST items, server returns the cart
POST /api/v3/carts
{ "items": [ ... ] }

Headers

Legacy baskets needed no key. channelId in the body only picked the retailer. Drop checksum; v3 has no equivalent. v3 needs the three headers in Authentication & Headers on every call.

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 (date and time), the inventory item, and the price option separately.

{
"items": [
{
"occurrenceId": "456",
"inventoryItemId": "v1_eyJwcm92aWRlcklkIjoxMDAwLCJzZWF0SWQiOiJPUkNIRVNUUkEtQS0xMDEifQ",
"priceOptionId": "eyJwcm92aWRlckNvZGUiOiJGVUxMUFJJQ0UiLCJob2xkQ29kZSI6IjE1NzM5In0",
"quantity": 1
}
]
}
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
channelIdX-TT-Retailer headerPicks the retailer; must be one your key was issued for

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 returned expiredAt, and status turned expired once the hold ran out. v3 returns expiresAt (UTC) on the cart — show a countdown. When it passes, the hold is released and GET /api/v3/carts/{cartId} returns 410 CART_EXPIRED (see Cart state). Create a new cart to continue. To release a hold early, DELETE /api/v3/carts/{cartId} (idempotent, returns 204; 404 for an unknown cart, 409 CART_ALREADY_ORDERED for one that became an order).

Discounts move to a voucher endpoint

Legacy applied a coupon inline on the basket or through applyPromotion. v3 takes the code as voucherCode when the cart is created, or through a dedicated pair:

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

The discount shows up in the cart’s receiptLines[] and total. Which route to use, and why an access code must go on POST /carts, is in Vouchers.

Changing seats means a new cart; changing extras does not

In v3 a cart’s seats and GA are fixed once created — to change them, create a new cart and pass the old cart’s id as previousCartId: once the new cart is created, the same request attempts to release the old hold so the customer isn’t left holding two sets of inventory. The release is best-effort and never fails the create — an old cart that is unknown, expired, or already turned into an order is left alone, and the old hold then simply times out on its own.

Extras are added, changed and dropped in place on a live cart, with POST, PATCH and DELETE on /api/v3/carts/{cartId}/items — see Add Extras.

Money format

// Legacy basket money
{ "value": 8500, "currency": "USD", "decimalPlaces": 2 }
// New cart total
{ "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)

{
"data": {
"id": "6aac14d3-5826-4da8-98b6-9e68c28629d6",
"expiresAt": "2026-03-15T19:40:00Z",
"total": { "amount": 18500, "currency": "USD" },
"items": [ ... ],
"receiptLines": [ ... ],
"options": {
"currencies": ["USD", "EUR"],
"extras": [ ... ],
"paymentMethods": [ { "method": "INVOICE" } ]
}
}
}

Key differences:

  • id is the server-generated cart ID — stable. Store it on your server only: it reads the order (see Keep the cart ID)
  • expiresAt is the hold deadline in UTC — drive a countdown from it
  • options.extras lists add-ons you can attach; it is an empty list when the event offers none
  • options.currencies lists the currencies the cart can be priced in
  • options.paymentMethods lists the methods the cart can be paid with; pick one at checkout (see Paying)

Migration Checklist

  1. Replace PATCH /api/v1/baskets with POST /api/v3/carts; let the server return the cartId
  2. Drop channelId; send X-TT-API-Key, X-TT-Retailer and X-TT-Platform on every call
  3. Convert reservations to a flat items list keyed by occurrenceId, inventoryItemId, and priceOptionId
  4. Create the cart with items from one occurrence; an extra with its own schedule can name its own (see Add Extras)
  5. Read expiresAt instead of expiredAt and show a countdown; handle 410 CART_EXPIRED by creating a new cart
  6. Move coupons to voucher codes on POST /api/v3/carts/{cartId}/vouchers
  7. Replace single-reservation removal with “create a new cart with previousCartId”, which makes a best-effort release of the old hold in the same request; use DELETE to release a hold without creating a new cart
  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. Checkout — POST /api/v3/orders (see Checkout → Orders)