Getting started
Errors
Errors use standard HTTP status codes and a stable, machine-readable error.code. Branch on the code; show the message to people.
Error shape
{
"data": null,
"meta": {},
"error": {
"code": "rate_unavailable",
"message": "This selected rate is no longer available. Refresh the hotel rates and choose another option.",
"details": []
}
}details is always an array. It lists each failed rule when a body or query fails validation, and is usually empty otherwise — including for other 400s that come back as validation_error, where message gives the reason.
Authentication and access
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 401 | unauthorized | Key missing (message Unauthorized), unknown, revoked or expired, or your agency is suspended or its API access is switched off. | Check the key; create a new one if needed. If the key is right, contact support. |
| 403 | forbidden | The resource belongs to another agency, or your key's access level doesn't allow the call. | Use a resource your agency owns, or an agency-admin key for key management. |
| 403 | api_key_owner_missing | The key was created before keys recorded who created them, so it can't search or book. | An agency admin creates a new key and you switch to it. |
| 403 | agreement_unsigned | Your agency hasn't signed the distribution agreement. | An agency admin signs it in the portal. |
Validation
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | validation_error | The body or query failed validation; details lists each problem, including unknown fields. Other rejected requests — a bad market on capabilities, say — also use this code, with an empty details and the reason in message. | Fix the request. |
| 400 | invalid_cursor | The search-results cursor is malformed. Other lists report a bad cursor as validation_error. | Use the cursor exactly as returned. |
| 400 | invalid_limit | limit on search results isn't a positive integer. | Send a positive integer; values above 500 are capped at 500. |
| 400 | unknown_destination | An activity destination name couldn't be resolved. | Send destinationCode from GET /destinations. |
| 400 | invalid_hotel_selection | hotelId was sent on a non-hotel search. | Remove it. |
| 400 | invalid_product_type | A hotel-only call was made on another product type. | Use the product rates route. |
| 404 | not_found | The ID doesn't exist, or has been purged. | Check the ID. |
Search and quotes
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 410 | search_expired | The search has passed its 20-minute life. Opening a rate sheet or price-checking restarts the clock. Booking can return it too, once the search behind the quote is gone. | Run a new search. |
| 410 | quote_expired | The quote's 20-minute life has ended. | Fetch fresh rates and price-check again. |
| 409 | quote_invalid | The quote has no usable price. | Fetch fresh rates. |
| 409 | unknown_supplier | The quote's inventory source can't be reached for a price-check right now. | Choose another rate. |
| 409 | rate_unavailable | The rate has sold out or is no longer offered. | Refresh the rate sheet and let the traveller choose again. |
| 409 | pax_mix_unavailable | An activity isn't available for the new traveller mix. | Choose another option. |
| 400 | activity_recheck_unsupported | The activity has no separate options to list. | Price-check the row directly. |
| 400 | rate_option_not_offered | The supplierRateRef sent with a price-check isn't one of this quote's options. | Send a token from this quote's activity options, unchanged. |
| 409 | price_check_currency_unavailable | The live price came back in a currency we can't convert to the quote's currency. | Choose another rate, or search again. |
| 409 | price_check_conflict | Another price-check on the same quote finished first. | Retry once. |
| 409 | quote_not_repriceable | The quote has already been booked or has expired. | Start from fresh rates. |
| 503 | supplier_unavailable | The inventory source didn't answer in time. | Retry with backoff, or choose another rate. |
Bookings
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | idempotency_key_required | No Idempotency-Key header. | Send a fresh UUID. |
| 409 | idempotency_in_progress | That key is still running, or was used with a different body. | Wait and retry, or use a new key for a new body. |
| 409 | quote_not_price_checked | The quote wasn't price-checked in the last 10 minutes, or another booking already claimed it. | Price-check, then book straight away. |
| 400 | changes_ack_required | The price-check reported a price or policy change you haven't acknowledged. | Show the change, then send changesAcknowledged: true. |
| 400 | package_ack_required | The rate may only be sold as part of a package. | Send packageRateAcknowledged: true only if you are selling it in a package. |
| 400 | traveler_manifest_mismatch | Travellers don't match the rooms, adults and child ages you searched for. | Send one traveller per searched guest with the right roomIndex and child ages. |
| 400 | supplier_contact_required | This inventory needs a valid email and a phone number for the lead traveller, and one is missing. | Always send both on travelers[0]. |
| 400 | traveler_title_required | This rate needs a title — on every traveller for a hotel quote with requiresGuestTitle: true, or on the lead traveller for some activities and transfers — and one is missing or not recognised. Refused before anything is charged. | Send title as one of Mr, Mrs, Ms, Miss, Master, Dr. See Titles. |
| 400 | travel_documents_required | A flight passenger is missing a name, date of birth, gender, passport number, passport expiry or nationality, or their passport expires within 6 months of travel. message names the traveller. Refused before anything is charged. | Complete the passenger's details, or ask them for a passport valid long enough. |
| 400 | transfer_flight_details_required | The transfer needs the flight (arriving or departing). | Send carrierName and flightNumber, at most 7 characters, like EK003. |
| 400 | pan_required | The activity rate needs an Indian PAN (details.panRequired is true) and panNumber is missing or malformed. | Send panNumber like ABCDE1234F. |
| 400 | unsupported_payment_method | The payment method isn't available for this booking. | Send paymentMethod: "credit". |
| 400 | booking_answer_required | A mandatory booking question wasn't answered. | Answer every question in details.bookingQuestions. |
| 400 | booking_answer_invalid | An answer isn't one of the allowed answers, or is too long. | Send one of the question's allowed answers, within its length limit. |
| 400 | hold_not_eligible | The rate can't be held — holds need a fully refundable hotel rate with a future free-cancellation deadline. | Book and pay now instead. |
| 409 | supplier_disabled | That inventory has been switched off since you searched. | Choose another rate. |
| 409 | live_supplier_booking_blocked | Sandbox only. Some inventory has no test environment, so outside production it only books rates that are still free to cancel. The rate is non-refundable or its free-cancellation window has closed. Never returned in production. | Pick a rate that's still free to cancel. |
| 409 | booking_invalid_transition | The booking can't move to that state — cancelling a failed booking, for example. | Read the booking's current state first. |
Payment and credit
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 402 | credit_limit_exceeded | Your agency's available credit doesn't cover the booking. | Top up or raise your limit in the portal. |
| 403 | credit_disabled | Credit payments are switched off for your agency. | Contact support. |
| 409 | wallet_currency_mismatch | The booking currency differs from your wallet currency. | Search in your wallet currency. |
| 409 | hold_payment_unsupported | A hold was paid with a method other than credit. | Pay the hold with paymentMethod: "credit". |
| 409 | payment_operation_in_progress | Another payment step on this booking is still running. | Wait a few seconds, then read the booking before retrying. |
| 409 | payment_authentication_pending | A card authentication was never completed. Card payments aren't available through the API. | Pay with credit. |
Rate limiting and platform
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 429 | rate_limited | Too many requests for this key in the last minute. | Wait for Retry-After seconds. |
| 409 | conflict | A generic conflict with no more specific code; message says what. | Read the resource's current state before retrying. |
| 410 | gone | A generic "no longer available" with no more specific code. | Start again from fresh data. |
| 500 | internal_error | Something failed on our side. | Retry with backoff; contact support with the time and endpoint if it persists. |