Skip to navigation

Error Handling

All API errors follow a consistent structure for programmatic handling. Parse type and code to determine what happened and how to recover.

This page covers the v3 structured error model. For the v1/v2 error and rate-limit model, see Errors and rate limits on the legacy portal.

Error Response Structure

{
"error": {
"type": "INVALID_REQUEST_ERROR",
"code": "PAYMENT_METHOD_NOT_AVAILABLE",
"message": "INVOICE is not available for this cart. Pay with a method the cart lists.",
"param": "payment.method",
"details": {
"availablePaymentMethods": ["CARD", "APPLE_PAY"]
}
}
}
FieldRequiredDescription
typeYesError category aligned with HTTP status (see table below)
codeYesMachine-readable error code for programmatic handling
messageYesHuman-readable description, in English. For display only — don’t switch on this.
paramNoRequest field or header that caused the error (JSON path notation, e.g., payment.method)
detailsNoStructured data for error recovery. Contents vary by error code.

Error Types

Each type maps to an HTTP status code:

TypeHTTP StatusWhen It Happens
INVALID_REQUEST_ERROR400Malformed request, invalid field values, missing required fields
AUTHENTICATION_ERROR401Missing or invalid API key
PAYMENT_ERROR402Payment was refused, or the shopper needs to authenticate before it can go through
PERMISSION_ERROR403Valid API key but the operation is not permitted — for this key, this customer or this order — or an order declined by risk checks
NOT_FOUND_ERROR404Resource doesn’t exist or you don’t have access
CONFLICT_ERROR409State conflict (inventory taken, cart already ordered)
RESOURCE_GONE_ERROR410Resource existed but expired (e.g., cart hold timed out)
RATE_LIMIT_ERROR429Rate limit exceeded
API_ERROR500Server-side error
SERVICE_UNAVAILABLE503Temporarily can’t serve the request; retry later, on POST /orders with a new Idempotency-Key

Common Error Codes

These tables cover every code on the flows these guides walk through. Codes for Apple Pay sessions are listed on those endpoints in the API Reference.

Authentication & Authorization

CodeTypeRecovery
UNAUTHORIZEDAUTHENTICATION_ERRORNo X-TT-API-Key header was sent, or the request needs a signed-in customer (for example paying with a saved card) and has none. Send your API key, or the customer’s token.
INVALID_API_KEYAUTHENTICATION_ERRORThe key is wrong or revoked, or X-TT-Retailer is missing or isn’t a retailer your key was issued for. Check both headers; regenerate the key in the partner portal if needed.
INSUFFICIENT_PERMISSIONSPERMISSION_ERRORThis key or customer isn’t allowed to do this: for example, INVOICE payment on a cart with nothing to pay from a key that isn’t your secret key, or reading an order with a cart ID it wasn’t placed with. Use the right credential, or contact support.
ORIGIN_NOT_ALLOWEDPERMISSION_ERRORThe request came from a browser origin your API key doesn’t list. Add the origin to the key, or use a key that lists it.

Headers & Request Validation

CodeTypeRecovery
MISSING_REQUIRED_HEADERINVALID_REQUEST_ERRORA required header is missing — usually X-TT-Platform. Send it.
MISSING_REQUIRED_FIELDINVALID_REQUEST_ERRORA required body field is missing; param names it when it can. Add it and send the request again.
INVALID_FIELD_VALUEINVALID_REQUEST_ERRORA field or header has a value that can’t be used; param names it when it can. Fix the value and send the request again.
INVALID_REQUESTINVALID_REQUEST_ERRORThe request can’t be used as sent: invalid JSON, an unsupported method, or an unusable value. Read message, fix the request or start a new cart; sending it unchanged fails the same way.

Carts (POST /carts and adding items)

CodeTypeRecovery
INVENTORY_EXHAUSTEDCONFLICT_ERRORReduce quantity or choose a different item
INVALID_PRICE_OPTIONINVALID_REQUEST_ERRORRefresh inventory and select a valid price option
OCCURRENCE_NOT_AVAILABLECONFLICT_ERRORChoose a different occurrence (date and time)
PRICE_CHANGEDCONFLICT_ERRORRefresh inventory, confirm the current price, and create a new cart.
ORPHANED_SEATSCONFLICT_ERRORChoose different seats that don’t leave a single seat on its own
MIXED_OCCURRENCES_NOT_SUPPORTEDINVALID_REQUEST_ERRORAll items must be from the same occurrence
MIXED_ITEM_TYPES_NOT_SUPPORTEDINVALID_REQUEST_ERRORGeneral admission and assigned seats can’t share a cart — use a cart for each
EVENT_NOT_SELLABLEINVALID_REQUEST_ERRORThe event is closed, or is sold only as an extra alongside another event. Choose a different event.
OCCURRENCE_NOT_COMPATIBLEINVALID_REQUEST_ERRORAn extra with its own schedule was added at an occurrence outside the times the cart lists for it. Pick one within requirements.occurrence.
MIXED_PRODUCT_TYPES_NOT_SUPPORTEDINVALID_REQUEST_ERRORGift cards and tickets check out separately — use a cart for each
MULTIPLE_GIFT_CARDS_NOT_SUPPORTEDINVALID_REQUEST_ERRORA cart holds one gift card; place a separate order for each card
GIFT_CARD_QUANTITY_NOT_SUPPORTEDINVALID_REQUEST_ERRORSend quantity: 1 for a gift-card item; ticket quantity is unaffected

Cart Vouchers (POST /carts/{cartId}/vouchers)

CodeTypeRecovery
INVALID_VOUCHERINVALID_REQUEST_ERRORThe voucher code is unknown, expired, or not valid for these items. Some codes are limited to one use per customer: for a guest, send the customer’s email with the code.
VOUCHER_NOT_APPLICABLE_TO_GIFT_CARDINVALID_REQUEST_ERRORA gift card is sold at its listed price; place the gift card in its own cart and apply the code to a ticket order instead

Currency

Returned when a requested currency can’t be used. Which value gets which code is in Currency.

CodeTypeRecovery
CURRENCY_NOT_SUPPORTEDINVALID_REQUEST_ERRORRequest one of details.supportedCurrencies when it is sent, or omit the currency for base-currency prices
CURRENCY_TEMPORARILY_UNAVAILABLESERVICE_UNAVAILABLEThe currency is sold here but can’t be priced right now (no current exchange rate). Retry shortly, or omit the currency for base-currency prices

Cart state

Every operation on an existing cart — reads included — answers CART_EXPIRED (410) for a cart that is gone (timed out, deleted, or no longer holding inventory) and CART_ALREADY_ORDERED (409) for a cart that became an order, including one since canceled; look that order up with GET /orders?cartId={cartId} (see After Checkout). details.orderId names it when it can be found. Deleting a cart is the exception: see Create Cart.

Checkout (POST /orders)

CodeTypeRecovery
EXPECTED_TOTAL_MISMATCHINVALID_REQUEST_ERRORRe-fetch cart, display current totals, confirm with customer
TICKET_LIMIT_EXCEEDEDCONFLICT_ERRORThe venue’s ticketing system limits how many tickets one customer can buy, counting earlier orders. Show message, which is the venue’s own wording and says how many tickets the customer has left, if any. Retrying the same order cannot succeed; a smaller order might.
INVALID_FIELD_VALUEINVALID_REQUEST_ERRORA field in the order has a value that can’t be used, and param names it — fix that field and place the order again with a new Idempotency-Key. A details.reason of ORDER_PHONE_NUMBER_INVALID (param: contact.phone) means the phone number can’t be used for the order: ask the customer to correct it, and send it in E.164 format (+12125550123). Only retailers with phone-number validation enabled receive it. A details.reason of ORDER_EMAIL_INVALID (param: contact.email) means the venue’s ticketing system won’t accept the email address, for example because its mailbox doesn’t exist: ask the customer to correct it. details.reason is not guaranteed on this code — a field that fails basic validation returns param without it — so handle INVALID_FIELD_VALUE without relying on details.reason.
PAYMENT_METHOD_NOT_AVAILABLEINVALID_REQUEST_ERRORThe order named a payment method or gateway the cart can’t be paid with, and nothing was charged. details.availablePaymentMethods lists the methods it can take: let the customer pick one, then place the order again with a new Idempotency-Key. An empty list means the cart can’t be paid for at all. An order with nothing to pay is never refused this way.
MISSING_REQUIRED_FIELDINVALID_REQUEST_ERRORThis retailer needs a phone number on every order, and contact.phone wasn’t sent (param: contact.phone). Collect one and place the order again with a new Idempotency-Key.
INVALID_VOUCHERINVALID_REQUEST_ERRORThe voucher on the cart failed its last check, made after the payment was authorized. Remove it with DELETE /carts/{cartId}/vouchers/{code}, read the cart again, and place the order with a new Idempotency-Key.
MINIMUM_QUANTITY_NOT_METINVALID_REQUEST_ERRORThe cart holds fewer of an extra than its minQuantityPerOrder. details carries the extra’s eventId, its minQuantityPerOrder and the quantity held: add more of the extra, or remove it, then place the order again with a new Idempotency-Key.
EVENT_MINIMUM_NOT_METINVALID_REQUEST_ERRORThe cart holds fewer tickets than the event’s orderLimit.min. details carries the event’s eventId, its minQuantityPerOrder and the quantity held: create a new cart with at least that many tickets, then place the order with a new Idempotency-Key.
CART_EXPIREDRESOURCE_GONE_ERRORCreate a new cart (hold timed out, or the cart no longer holds inventory)
CART_ALREADY_ORDEREDCONFLICT_ERRORThe cart already became an order (possibly since canceled). Don’t buy again — look it up with GET /orders?cartId={cartId}.
PAYMENT_AUTHENTICATION_REQUIREDPAYMENT_ERRORThe shopper has to complete a step with the payment provider first: a card issuer’s challenge, or approving a KLARNA or AFTERPAY payment. details.action holds the provider’s action as a JSON string, always present on this code — parse it and pass it to your payment SDK (an action of type redirect sends the shopper to its url), then place the order again with the result under a new Idempotency-Key.
PAYMENT_DECLINEDPAYMENT_ERRORThe issuer refused the payment. Show message and let the customer try a different payment method. A payment that failed because the provider or network failed is a 5xx instead, since another card won’t help.
INSUFFICIENT_PERMISSIONSPERMISSION_ERRORTwo causes. The customer isn’t allowed to buy: show message, which is written for the customer, and don’t retry. Or the key can’t use this payment method, such as INVOICE from a key that isn’t your secret key: see Authentication & Authorization and use the right credential.
RISK_CHECK_FAILEDPERMISSION_ERRORThe order was declined by risk checks. Not a payment problem: retrying — with the same or a different payment method — will not succeed. Show message and direct the customer to contact customer support.

A KLARNA or AFTERPAY order completes on a second call, made after the shopper approved the payment. If that call is refused because the payment can no longer become this order — CART_EXPIRED, EXPECTED_TOTAL_MISMATCH, PAYMENT_METHOD_NOT_AVAILABLE, or the cart’s currency no longer being available — the approved payment is canceled before the answer arrives, so the shopper approves again from the first call. A refusal you can correct, such as a missing phone number or the wrong expectedTotal.currency, leaves the approval open: fix the request and send it again with the same approval result under a new Idempotency-Key.

Idempotency (POST /orders)

CodeTypeRecovery
INVALID_IDEMPOTENCY_KEYINVALID_REQUEST_ERRORThe Idempotency-Key isn’t a UUID v4. Generate one and send the order again.
IDEMPOTENCY_KEY_ALREADY_PROCESSINGCONFLICT_ERRORAn earlier request with this key is still running. Wait a moment, then send the same request with the same key.
IDEMPOTENCY_KEY_FAILEDCONFLICT_ERRORAn earlier request with this key failed, so the key can’t be used again. Send the order with a new key.
IDEMPOTENCY_KEY_CART_MISMATCHCONFLICT_ERRORThis key was already used with a different cart. Use a new key for each cart.

General

CodeTypeRecovery
RESOURCE_NOT_FOUNDNOT_FOUND_ERRORVerify the ID is correct; resource may have been deleted
INTERNAL_SERVER_ERRORAPI_ERROR (500)Something failed on our side. Retry as described in Retry Strategy, and contact support with the X-Correlation-ID if it keeps happening.
SERVICE_UNAVAILABLESERVICE_UNAVAILABLE (503)A service the request needs is down for a moment. Retry later.
THIRD_PARTY_OUTAGESERVICE_UNAVAILABLE (503)A ticketing or payment system the request depends on isn’t answering. Retry later. On POST /orders the same outage arrives as a 500 INTERNAL_SERVER_ERROR.
RATE_LIMITEDRATE_LIMIT_ERRORWait at most the number of seconds in the Retry-After header when it is present, then retry; without it, back off as described in Retry Strategy. Any endpoint can return it — see Rate limits.

Rate Limits

Each API key may make a set number of requests per retailer in a window, across every endpoint. The limit sits far above what a storefront needs, and exists to stop a key that is leaked or stuck in a retry loop. Once a key reaches it, every endpoint answers 429 RATE_LIMITED for that retailer until the window ends.

A response counted against the limit carries two headers. Read them when they are there, but don’t depend on them: handle 429 whether or not you saw them.

HeaderMeaning
X-RateLimit-LimitHow many requests this API key may make for this retailer in the current window.
X-RateLimit-RemainingHow many of those requests are left in the current window.

A response served from our CDN cache never reaches the API, so it is not counted and repeats the headers of the response that was cached. Read X-RateLimit-Remaining on a cached response as a guide, not a live count.

A 429 from this limit also carries Retry-After, the most seconds to wait before sending the request again. A 429 for any other reason may not; see Retry Strategy. A key serving several retailers has a separate limit for each retailer it sends in X-TT-Retailer.

Retry Strategy

Safe to Retry

  • 5xx errors — server-side issues are usually transient, including 503 CURRENCY_TEMPORARILY_UNAVAILABLE (a sold currency with no current exchange rate). Retry with exponential backoff (1s, 2s, 4s, max 3 attempts). On POST /orders, a 5xx uses up the Idempotency-Key, so retry with a new key; if the first attempt did place the order, you get 409 CART_ALREADY_ORDERED instead of a second order.
  • 429 Rate Limit — wait at most the number of seconds in the Retry-After header before retrying. When the header is absent the delay is not known; back off as for a 5xx.
  • Network timeouts on POST /orders — you don’t know whether the order went through, so send the same request with the same Idempotency-Key. You get one of these answers:
    • The order, with 200 rather than 201 — the first attempt succeeded, and this is the order it placed.
    • 409 IDEMPOTENCY_KEY_ALREADY_PROCESSING — the first attempt is still running. Wait, then send the same request again.
    • 409 IDEMPOTENCY_KEY_FAILED — the first attempt failed. Send the order with a new key.
    • 409 CART_ALREADY_ORDERED — the key is over 24 hours old, so it no longer remembers the attempt, and the cart already became an order. Look it up with GET /orders?cartId=.

Not Safe to Retry (Without Changes)

  • 400 errors — fix the request before retrying (wrong field value, missing field, invalid quantity).
  • 401/403 errors — fix authentication or permissions (RISK_CHECK_FAILED is the exception — see below).
  • 403 RISK_CHECK_FAILED — the order was declined by risk checks. Retrying will not succeed, with the same or a different payment method. Show message and direct the customer to contact customer support.
  • 402 PAYMENT_AUTHENTICATION_REQUIRED — run the challenge from details.action first, then place the order again with the result. Use a new Idempotency-Key: the first attempt retired the old one, so reusing it returns 409 IDEMPOTENCY_KEY_FAILED. Nothing was charged, and the cart is still held.
  • 402 PAYMENT_DECLINED — the card was refused, and retrying the same card will be refused again. Collect a different payment method, then place the order again with a new Idempotency-Key.
  • 409 Conflict — state has changed; re-fetch the resource and adapt.
  • 410 Gone — resource is permanently unavailable; start over (e.g., create a new cart).

Idempotency Keys

The Idempotency-Key header on POST /orders is optional and strongly recommended. Without it, a cart still becomes at most one order — a second attempt gets 409 CART_ALREADY_ORDERED — but you can’t tell a lost response from a failure. With it:

  • Generate a UUID v4 for each purchase attempt. A value that isn’t a valid UUID v4 is rejected with INVALID_IDEMPOTENCY_KEY.
  • Reuse the key only to resend the same request after a timeout. See Network timeouts for the answers you can get.
  • After an error response that lets you try again, place the order again with a new key. Most of these, including a 400 for a field you then fix, use up the key, and resending it returns 409 IDEMPOTENCY_KEY_FAILED. Do not retry 403 RISK_CHECK_FAILED. After 409 CART_ALREADY_ORDERED, look the order up with GET /orders?cartId={cartId} instead of buying again. A 5xx does not prove nothing was placed, so a new key is safe only because the cart can become just one order. Reuse a key only when no response came back, or after 409 IDEMPOTENCY_KEY_ALREADY_PROCESSING.
  • Same key + different cartId → 409 IDEMPOTENCY_KEY_CART_MISMATCH.
  • Keys belong to your retailer and expire after 24 hours.
POST /api/v3/orders
Idempotency-Key: 3f8c1e2a-9d4b-4f6a-8b21-7c5e0a1d2e34

Forward Compatibility

The API may introduce new error codes and types at any time. To avoid breaking your integration:

  • Switch on type for broad error handling (authentication vs validation vs server error)
  • Switch on code for specific recovery logic (e.g., CART_EXPIRED → create new cart)
  • Use a default/fallback case for unknown type and code values
  • Display message to users but never parse it programmatically — it may change