Skip to navigation

API Conventions

Patterns and standards that apply across all TTG API v3 endpoints. Read this first — individual endpoint docs assume familiarity with these conventions.

Response Envelope

All responses use a consistent envelope:

Success:

{
"data": { ... }
}

Success (list):

{
"data": [ ... ],
"pagination": { "limit": 50, "offset": 0, "total": 142 }
}

Error: an error object instead of data. Its fields are in Error Handling.

A body holds data or error, never both. Lists add pagination beside data, and some add more, such as filters on occurrences. A 204 answer, such as from DELETE /api/v3/carts/{cartId}, has no body. Parse the HTTP status code first (2xx vs 4xx/5xx), then read the body.

Money Format

All monetary values use integer minor units with an ISO 4217 currency code. No floating-point, no strings.

{
"amount": 18500,
"currency": "USD"
}

The number of decimal places depends on the currency:

CurrencyScaleamount: 18500 means
USD, GBP, EUR2$185.00
JPY, KRW0¥18,500

Use your platform’s i18n library to determine the scale from the currency code — don’t hardcode / 100.

Client examples:

  • JavaScript: new Intl.NumberFormat(locale, { style: "currency", currency }).format(amount / 10 ** scale)
  • iOS: NumberFormatter with currencyCode set
  • Android: Currency.getInstance(currency).defaultFractionDigits for scale

Currency

Every price can be shown and charged in a local currency. The default is the event’s base currency: the currency the event is priced in, which is its location’s own currency.

The rule: reads take a currency query parameter (display); writes take a currency body field (state).

Browsing. On occurrences (an event’s dates and times) and inventory-items, send ?currency= (ISO 4217) to see prices in that currency; omit it for the base currency. This affects display only. An event’s fromPrice is always in its base currency, so GET /api/v3/events takes no currency.

Which currencies can I ask for? Each location on GET /api/v3/locations lists its availableCurrencies — the base currency first, then the alternatives available to you. Build currency pickers from this list rather than probing. The same list also comes back as details.supportedCurrencies when 400 CURRENCY_NOT_SUPPORTED rejects a currency the retailer doesn’t sell (an unrecognized code is rejected before retailer lookup and carries no details).

A cart owns its currency. A cart is created in a currency — base by default, or the body’s currency when supported. The cart’s current currency is total.currency on the response; options.currencies lists the currencies it can be priced in.

  • Quote: GET /api/v3/carts/{cartId}?currency=EUR returns the cart priced in euros, without changing the cart. total.currency then says EUR, not the cart’s own currency, so a quoted total won’t pass as expectedTotal until you switch the cart. Read it again without the parameter and you get the cart’s own currency back.
  • Switch: PATCH /api/v3/carts/{cartId} with { "currency": "EUR" } changes the currency the cart is priced and settled in. Later reads come back in euros with no parameter. Send the event’s base currency to go back to base prices.

Prices are always current. Every amount is worked out from the exchange rate at the time you read the cart, so a total can move while the cart is open. Read the cart again just before checkout and send that amount as expectedTotal.

total is the amount owed — don’t derive it from the items, the receipt lines, or the voucher.

Orders confirm the cart. POST /api/v3/orders settles in the cart’s currency — the order call takes no currency input, so switch currency on the cart (PATCH) beforehand. expectedTotal must equal the cart total exactly, both amount and currency. A different amount returns 400 EXPECTED_TOTAL_MISMATCH: read the cart again and place the order with the new total, under a new Idempotency-Key if you sent one. A different currency returns 400 INVALID_FIELD_VALUE on expectedTotal.currency.

Asking for a currency the retailer doesn’t sell the event in returns 400 CURRENCY_NOT_SUPPORTED. So does a code the API doesn’t know: it knows a fixed list of currencies, so even a real ISO 4217 code outside it (such as INR) gets this error. A value that isn’t three capital letters (such as gbp) returns 400 INVALID_FIELD_VALUE. A currency the retailer does sell but that can’t be priced right now (no current exchange rate) returns 503 CURRENCY_TEMPORARILY_UNAVAILABLE — retry shortly. This applies wherever a currency is accepted: browsing, cart create (POST), cart quote (GET), and cart switch (PATCH). Asking for nothing prices in the cart’s own currency — the base currency unless the cart was created in or switched to another one. A requested currency is never silently swapped for base.

Example — a London show (base GBP), customer paying in euros: create the cart, PATCH it with { "currency": "EUR" }, read the cart to get the current euro total, then place the order with expectedTotal set to it — e.g. { "amount": 4899, "currency": "EUR" }.

Party Size

Browse reads take a ?quantity= query parameter (a positive integer) — the number of tickets the party needs. Omit it to see everything.

  • Inventory-items: general admission areas without enough tickets drop out, and so do most seats the party can’t buy. A seat that stays listed can still be unavailable at that size: Seats for a party explains how to tell.
  • Occurrences: pricing and availabilityLevel reflect only that inventory. An occurrence without enough seats together reads as SOLD_OUT, or is left out when the event or your retailer account is set to list only dates with something on sale.
  • Changing the count: call again when the customer changes the ticket count. Pagination applies to the filtered list, so fetch every page with the same quantity.

IDs

All IDs are opaque strings. Never parse, construct, or derive meaning from an ID — treat them as tokens you receive from one endpoint and pass to another. The format may change without notice.

Stable IDs — safe to store:

  • Events, Occurrences, Carts, Orders, Tickets

A cart ID is also a credential: store it on your server only — it reads the order. See Keep the cart ID.

Re-fetch, don’t store — these don’t expire and aren’t tied to a session, but they point at seats and prices that change. A price can change while its price option ID stays the same, so an unchanged ID doesn’t mean an unchanged price. Re-fetch inventory just before adding to a cart:

  • Inventory Items, Price Options

Pagination

All v3 list endpoints — events, occurrences, inventory-items, locations, and orders by cart (GET /api/v3/orders?cartId=) — use limit/offset pagination and return a pagination block.

ParameterTypeDefaultDescription
limitinteger50Max results per page (1–1000)
offsetinteger0Number of results to skip

The response includes a pagination object:

{
"pagination": {
"limit": 50,
"offset": 0,
"total": 142
}
}

To iterate all pages: increment offset by limit until offset >= total.

Timezones

Occurrence times (startsAt, endsAt) use the venue’s local timezone with UTC offset:

2026-03-15T19:30:00-04:00

Use the venue.timezone field (IANA identifier, e.g., America/New_York) if you need an IANA timezone for calendar integration (Apple Calendar, Google Calendar). It can be null; then fall back to the offset in startsAt. For display, the offset embedded in the timestamp is sufficient — no timezone manipulation required.

System timestamps (createdAt, expiresAt) use UTC:

2026-03-15T19:40:00Z

Whole days are plain yyyy-MM-dd dates: an event’s startDate and endDate, and expiresOn (the last day it can be used) on a cancellation voucher (an order’s cancellationVoucher) or a gift card, such as 2027-04-20.

Nullability

Fields are nullable only when absence carries meaning:

  • availabilityLevel: null — no availability signal; display the occurrence normally
  • fromPrice: null — nothing is on sale for this occurrence (sold out, off sale, or not yet on sale); it can’t be bought
  • endsAt: null — the occurrence has no fixed end time
  • startDate: null — the event’s run dates aren’t announced yet
  • endDate: null — the event’s run is open-ended
  • barcode: null — barcode not yet available (delayed delivery venues)

When a nullable field is null, handle it explicitly in your UI — don’t display “null” or “$0.00”.

Optional fields that carry no information are omitted entirely rather than sent as null. For example:

  • maxDiscountPercentage — absent when no discount exists, or the event doesn’t show savings as a percentage
  • listPrice — absent when there’s no discount to show (see Price Options)
  • totalTaxes — absent when the taxes on a cart or order add up to zero

Forward Compatibility

The API may add new fields, enum values, or error codes at any time without a version bump. To avoid breaking:

  • Ignore unknown fields — don’t fail on unexpected keys in response objects
  • Handle unknown enum values — use a default/fallback case for enums like status, _type, and error code
  • Don’t switch on error messages — use type and code for programmatic handling; message is for display only and may change