Connector API · v1

API reference

One REST API for every supplier on Supplier.Travel. Your system calls Connector. Connector calls each supplier your account can use, normalises the answers into one contract and keeps the booking record. Supplier connections, credentials and protocols are operated by Supplier.Travel, so nothing in this reference changes when a supplier is added.

EnvironmentBase URLKeys
Sandboxhttps://sandbox.connector.supplier.travelSandbox plan keys; test inventory; bookings are never charged
Productionhttps://connector.supplier.travelGrowth and Enterprise plan keys

All requests and responses are JSON over HTTPS (TLS 1.2+). Contracts are versioned per vertical (/hotels/v1, /flights/v1). Additive fields can appear at any time, so ignore fields you don't know. Breaking changes ship only as a new major version.

Quickstart

  1. Create an agency account. It starts on the free Sandbox plan.
  2. In the console, open API keys and create a key. Copy it once; it is never shown again.
  3. Search hotels:
curl https://sandbox.connector.supplier.travel/hotels/v1/search \
  -H "Authorization: Bearer $CONNECTOR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "checkin": "2026-12-10", "checkout": "2026-12-12",
    "rooms": [{ "adults": 2 }],
    "destination": { "city_name": "Dubai", "country_code": "AE" },
    "currency": "USD", "guest_nationality": "AE"
  }'

Then prebook a rates[].id from the response and book the prebook. The booking flow walks through each step.

OpenAPI 3.1. The full contract is at /docs/openapi.yaml. Import it into Postman or Insomnia, or generate a typed client.

Authentication

Send your API key as a bearer token (or as X-API-Key):

Authorization: Bearer stk_9Kd2ZwBe…

Conventions

Money

Every amount is an integer in the currency's minor unit. decimals is the exponent: USD 2, KWD/OMR/BHD 3, JPY 0. Divide by 10^decimals for display. Nothing is hidden: total = supplier_total + platform_fee + markup.

"price": {
  "currency": "USD", "decimals": 2,
  "base": 17829,          // supplier base before included taxes
  "taxes": 3922,          // taxes and fees included in supplier_total
  "supplier_total": 21751,
  "platform_fee": 0,
  "markup": 544,          // your account markup
  "total": 22295
}

pay_at_property[] lists mandatory fees that are not in total and are collected by the hotel, such as tourism fees. Show them before checkout.

Identifiers

Ids are opaque strings such as nuitee~lp1b30d1, pb_7sptjqj2j3qre5iy or bk_vnio3epwl2pccmcx. Store them as text; rate ids can exceed 1,000 characters, so they always travel in JSON bodies.

Dates and times

Stay dates are YYYY-MM-DD. Timestamps are RFC 3339 in UTC (2026-12-08T10:00:00Z).

Partial results

Searches go to all your suppliers in parallel under one deadline. Every search response includes supplier_status[], one entry per supplier, with an outcome: ok, no_results, timeout, error, circuit_open, overloaded or skipped. partial: true means at least one supplier did not answer completely. A failed supplier never contributes results, so "no availability" and "supplier unavailable" are always distinguishable.

Request ids

Every response carries X-Request-Id. Send your own ([A-Za-z0-9._-]{1,64}) to correlate logs, and quote it when contacting support.

Limits

LimitValue
Request body64 KB
Search deadlineUp to 12 s end to end; slow suppliers are reported as timeout
Booking deadlineUp to 90 s; see unknown outcomes
Prebook validity20 minutes
API keys per agency20 active
Rate limitsPer plan. 503 SUPPLIER_RATE_LIMITED with retryable: true means back off and retry

Hotels — booking flow

  1. Destination GET /hotels/v1/destinations?q=: autocomplete for cities, hotels and landmarks. Each suggestion carries a ready-made destination object.
  2. Search POST /hotels/v1/search: priced offers across every supplier available to you, stored as a search session. The response holds page 1, facets for filters, and a search_id.
  3. Refine GET /hotels/v1/searches/{search_id}: filter, sort and page the session for 20 minutes without calling suppliers again.
  4. Hotel page GET /hotels/v1/hotels/{id} and …/reviews: photos, room types, facilities, policies and guest reviews.
  5. Prebook POST /hotels/v1/prebooks: the supplier confirms price and conditions, valid for 20 minutes.
  6. Book POST /hotels/v1/bookings with an Idempotency-Key. The agency net is charged to your account.
  7. Service: list, retrieve, refresh, change the lead guest, quote and cancel.
Search prices are indicative. Every offer has price_recheck_required: true. Always prebook, and charge the traveller the prebook price.

Destinations

GET/hotels/v1/destinations?q=dubai%20mar&limit=8
{ "suggestions": [
  { "type": "city", "id": "city:AE:Dubai", "name": "Dubai", "subtitle": "United Arab Emirates",
    "country_code": "AE", "hotel_count": 8209, "destination": { "city_name": "Dubai", "country_code": "AE" } },
  { "type": "place", "id": "place:ChIJ…", "name": "Dubai Marina", "subtitle": "Dubai, AE",
    "destination": { "place_id": "ChIJ…" } },
  { "type": "hotel", "id": "hotel:nuitee~lp1b30d1", "name": "Rove Dubai Marina", "subtitle": "Dubai, AE",
    "destination": { "hotel_ids": ["nuitee~lp1b30d1"] } } ] }

Matching ignores case and accents. Cities and hotels come from our synced master data and are instant; areas and landmarks come from the supplier. Pass a suggestion's destination unchanged into search. limit is 1–25, default 10.

POST/hotels/v1/search
{
  "checkin": "2026-12-10",
  "checkout": "2026-12-13",
  "rooms": [ { "adults": 2, "children_ages": [5] } ],
  "destination": { "country_code": "AE", "city_name": "Dubai" },
  "currency": "USD",
  "guest_nationality": "AE",
  "max_hotels_per_supplier": 100,
  "max_rates_per_hotel": 20,
  "sort": "recommended",
  "page_size": 20,
  "filters": { "star_ratings": [4, 5], "refundable_only": true }
}
FieldRules
checkin, checkoutCheck-in from today to 12 months ahead; 1–30 nights
rooms[]1–8 rooms. adults 1–8; children_ages 0–17, up to 6 per room
destinationExactly one of: {country_code, city_name}; {place_id} from destinations; {hotel_ids: […]} (up to 200); {latitude, longitude, radius_m} (1,000–50,000 m); {iata_code}
currencyISO 4217
guest_nationalityISO 3166-1 alpha-2 of the lead guest. It changes price and availability, so send the real value
max_hotels_per_supplier1–200, default 30
max_rates_per_hotel1–50, default 10
sortrecommended (default), price_asc, price_desc, stars_desc, review_desc, distance_asc
page_size1–100, default 20
filtersOptional; see filters. Star rating and minimum score are also sent to suppliers that support them
suppliersOptional subset of supplier ids; default is every hotel supplier available to you

Response:

{
  "request_id": "req_8d96fda5", "service": "hotels", "schema_version": "1.0",
  "generated_at": "2026-10-09T05:25:10Z",
  "search_id": "srch_054c05d48b0b6fe4899f0acb", "expires_at": "2026-10-09T05:45:10Z",
  "nights": 3, "partial": false,
  "supplier_status": [ { "supplier_id": "nuitee", "outcome": "ok", "duration_ms": 11821, "offer_count": 169 } ],
  "sort": "recommended", "filters": { … }, "page": 1, "page_size": 20,
  "total_offers": 109, "total_hotels": 169,
  "facets": {
    "star_ratings": { "0": 7, "3": 19, "4": 43, "5": 100 },
    "board_types": { "room_only": 147, "breakfast": 30, "half_board": 2 },
    "refundable": 109, "price_min": 17800, "price_max": 533400, "currency": "USD",
    "property_types": [ { "id": 204, "name": "Hotels", "count": 131 } ],
    "chains": [ { "id": 1763, "name": "Jumeirah", "count": 6 } ]
  },
  "offers": [ {
    "id": "nuitee~lp40986", "supplier_id": "nuitee", "price_recheck_required": true,
    "hotel": { "id": "nuitee~lp40986", "name": "Holiday Inn Express Dubai Internet City", "star_rating": 3,
               "review_score": 8.4, "review_count": 4289, "address": "Knowledge Village",
               "city": "Dubai", "country_code": "AE", "latitude": 25.1042, "longitude": 55.1662,
               "distance_m": 483, "chain": "Holiday Inn Express", "property_type": "Hotels",
               "thumbnail": "https://…", "main_photo": "https://…" },
    "from_price": { "currency": "USD", "decimals": 2, "base": 23518, "taxes": 0,
                    "supplier_total": 23518, "platform_fee": 0, "markup": 706, "total": 24224 },
    "rates": [ {
      "id": "nuitee~3gAYonJz…", "name": "Standard King Room",
      "board_code": "RO", "board_name": "Room Only", "board_type": "room_only",
      "rooms": [ { "room_number": 1, "name": "Standard King Room", "adults": 2, "children_ages": [5],
                   "max_occupancy": 3, "room_type_id": "1321525043" } ],
      "cancellation": { "refundable": true, "free_cancellation_until": "2026-12-08T10:00:00Z",
                        "penalties": [ { "from": "2026-12-08T10:00:00Z", "amount": 23518, "currency": "USD" } ] },
      "pay_at_property": [ { "description": "Tourism fee", "amount": 816, "currency": "USD" } ],
      "price": { … }, "price_per_night": 8075, "suggested_retail": 27100,
      "payment_type": "prepaid", "perks": ["Free WiFi"]
    } ]
  } ]
}

Refine a search

GET/hotels/v1/searches/{search_id}?star_ratings=4,5&board_types=breakfast&sort=price_asc&page=2

Returns the same response shape for the stored session, with no supplier calls, so it answers in milliseconds. Every filter is a query parameter; lists are comma-separated. A session belongs to the account that created it and expires at expires_at (20 minutes); after that you get 410 SEARCH_EXPIRED and should search again.

FilterMeaning
star_ratingsAny of 1–5; 0 matches unrated properties
min_review_score0–10
board_typesKeeps hotels with at least one rate on these meal plans, and only those rates
refundable_onlytrue keeps refundable rates only
price_min, price_maxTotal stay of the cheapest matching rate, in minor units of the search currency
hotel_nameCase-insensitive contains
chain_ids, property_type_ids, facility_idsIds from facets or reference data
max_distance_mUpper bound on distance_m
sort, page, page_sizeAs in search

Hotel page

GET/hotels/v1/hotels/{hotel_id}

Returns every hotel field plus description (plain text), images[], facilities[], checkin_from, checkout_by, phone, important_information, reviews_url, and:

"rooms": [ { "id": "1321525043", "name": "Standard King Room with Sofa Bed", "size_sqm": 26,
             "max_adults": 2, "max_children": 2, "max_occupancy": 3,
             "beds": [ { "type": "Extra-large double bed", "quantity": 1, "size": "181-210 cm wide" } ],
             "amenities": ["Free WiFi", "Rain shower", …], "views": ["City view"], "images": [ { "url": "https://…" } ] } ],
"policies": { "children_allowed": true, "pets_allowed": false, "parking": "free" }

Content is cached for 24 hours.

GET/hotels/v1/hotels/{hotel_id}/reviews?limit=20
{ "hotel_id": "nuitee~lp40986", "total": 4289, "average_score": 9.5,
  "reviews": [ { "score": 10, "name": "Ahmad", "country": "AE", "traveller_type": "solo traveller",
                 "date": "2026-09-19T00:00:00Z", "headline": "…", "pros": "…", "cons": "…", "language": "en-gb" } ] }

total is the property's lifetime review count. average_score averages only the reviews returned; show the hotel's review_score as the headline score. limit is 1–100. Reviews are cached for 6 hours.

Master data

GET/hotels/v1/content/hotels?country_code=AE&city_name=Dubai&q=marina&page=1&page_size=100
GET/hotels/v1/content/cities?country_code=AE
GET/hotels/v1/content/reference

Static content synced daily from suppliers, for building your own catalogue, mapping and filters. Hotels carry ids, name, address, coordinates, stars, review score and count, chain, property type, facility ids and photos, and are paged up to 1,000 at a time with a total. Cities include hotel_count. Reference data lists countries, facilities, chains and property_types with the ids used by filters. Only suppliers available to your account are included. Prices and availability are never cached: they always come from search.

Prebook

POST/hotels/v1/prebooks
{ "rate_id": "nuitee~3gAYonJz…" }

201 Created returns the prebook: id (pb_…), hotel_id, checkin, checkout, rooms[], board, cancellation, pay_at_property[], price, suggested_retail, terms, expires_at and three change flags: price_changed, cancellation_changed and board_changed. If any flag is true, show the new terms before booking. A rate that has gone returns 409 SUPPLIER_REJECTED_REQUEST; search again.

Book

POST/hotels/v1/bookings

Requires an Idempotency-Key header: 8–128 characters of A-Z a-z 0-9 . _ : -. Create one per checkout and store it with your order, so every retry of that checkout reuses it.

Idempotency-Key: order-4815-attempt-1

{
  "prebook_id": "pb_7sptjqj2j3qre5iy",
  "client_reference": "agency-ref-1001",
  "holder": { "first_name": "Ana", "last_name": "Lee", "email": "ana@example.com", "phone": "+971500000000" },
  "guests": [ { "room_number": 1, "first_name": "Ana", "last_name": "Lee", "remarks": "late arrival" } ]
}

Send exactly one lead guest per prebooked room. Use legal names: letters, spaces and ' . -, up to 50 characters. client_reference is your own id; it is echoed back and searchable.

Payment. Rates are payment_type: "prepaid": the agency net (price.supplier_total + price.platform_fee) is held on your account when the booking starts, charged when the supplier confirms and released if it fails. Your markup is never charged; you collect it from the traveller. Without enough available credit the call returns 402 INSUFFICIENT_CREDIT before any supplier call.

HTTPstatusWhat to do
201confirmedDone. Show hotel_confirmation_code and supplier_booking_id
202unknownThe supplier didn't answer in time and the booking may exist. Poll GET …?refresh=true. Never rebook with a new key. The amount stays on hold until resolved
422failedThe supplier did not book; error explains why and the hold is released. Prebook again to retry
200anyReplay of the same key and body: the original booking, without a new supplier call
402—INSUFFICIENT_CREDIT: top up or ask for a higher credit limit
422—IDEMPOTENCY_KEY_REUSED: same key with a different body. ACCOUNT_CURRENCY_MISMATCH: book in your account currency
409—PREBOOK_ALREADY_USED: another booking already used this prebook
410—PREBOOK_EXPIRED: prebook again

Retrieve and list

GET/hotels/v1/bookings/{id}
GET/hotels/v1/bookings/{id}?refresh=true
GET/hotels/v1/bookings?status=confirmed&checkin_from=2026-12-01&checkin_to=2026-12-31&q=lee&page=1&page_size=50

The first form returns the stored booking. refresh=true re-reads it from the supplier; for an unknown booking it looks up our reference at the supplier, and after 30 minutes with no match the booking becomes failed. The list returns {bookings, total, page, page_size}, newest first, for API and console bookings alike. q matches the booking id, your reference, the supplier reference, the hotel and guest names. page_size is 1–500.

Change the lead guest

POST/hotels/v1/bookings/{id}/amend
{ "holder": { "first_name": "Tess", "last_name": "Lee", "email": "tess@example.com" }, "remarks": "name correction" }

Sends a name change to the supplier and returns the booking with a new entry in amendments[] (status: "requested"). Suppliers process it asynchronously; refresh to see the result. Dates, rooms and rates cannot be amended: cancel and rebook. Only confirmed bookings can be amended (409 NOT_AMENDABLE).

Cancel

GET/hotels/v1/bookings/{id}/cancellation-quote
{ "booking_id": "bk_23bkvteay6qvpika", "cancellable": true, "free_cancellation_until": "2026-11-18T08:01:00Z",
  "fee": 0, "refund": 20102, "currency": "USD", "as_of": "2026-10-08T11:22:37Z",
  "note": "Estimate from the cancellation policy agreed at booking; the supplier confirms the final fee." }

Show the quote before the user confirms. The fee is the latest penalty that applies now.

POST/hotels/v1/bookings/{id}/cancel

Cancels a confirmed booking under the supplier's policy. The response is the booking with status: "cancelled" and cancellation_result {cancelled_at, fee, refund, currency}. The refund is credited to your account (never more than was charged). Refund your markup under your own terms. A repeat cancel is a no-op. An unknown booking returns 409 NOT_CANCELLABLE until a refresh resolves it.

Account and statement

GET/v1/account
GET/v1/account/statement?limit=100
{ "account": { "id": "org_t8e1akym0r_h", "currency": "USD", "credit_limit": 1000000, "balance": -20102,
               "held": 0, "available": 979898, "updated_at": "…" },
  "entries": [ { "id": "le_61b9…", "at": "…", "type": "charge", "amount": -20102, "currency": "USD",
                 "booking_id": "bk_23bk…", "note": "booking confirmed", "actor": "api:stk_Q3pX7aLm", "balance_after": -20102 } ] }

available = credit_limit + balance − held. Entry types are topup, adjustment, charge and refund; holds appear in held. actor is the console user or api: plus the key prefix that made the booking.

POST/flights/v1/search
{
  "slices": [ { "origin": "DXB", "destination": "LHR", "departure_date": "2027-02-15" } ],
  "passengers": { "adults": 1, "children": 0, "infants": 0 },
  "cabin_class": "economy",
  "currency": "AED",
  "market": "AE",
  "max_offers_per_supplier": 50
}

One slice is one-way, two make a return trip, and more make multi-city (up to 6). The response uses the same envelope with offers[]. Each offer has itineraries[].segments[] (carriers, flight number, airports, local times, cabin, booking class, aircraft), fare (brand, fare basis, refundable, changeable, baggage), price and expires_at. Flight booking is on the roadmap.

Suppliers

GET/v1/suppliers

Lists the suppliers available to your account, their services and their health:

{ "suppliers": [ { "id": "nuitee", "name": "Nuitee", "kind": "nuitee", "api_version": "liteapi-v3.0",
                   "services": ["hotels"], "circuit_state": "closed" } ] }

circuit_state is closed when healthy and open while a supplier is temporarily skipped after repeated failures. Which suppliers you see depends on your plan and on each supplier's distribution settings, and can change without any change on your side.

GET/healthz

Liveness check; no authentication.

Objects

Booking

FieldDescription
idConnector booking id (bk_…). Use it for every follow-up call
statuspending → confirmed | failed | unknown; confirmed → cancelled
supplier_id, supplier_booking_idWho fulfils it and their reference
hotel_confirmation_codeCode the hotel recognises, when provided
client_referenceYour reference, echoed
hotel_id, hotel_name, checkin, checkout, rooms[], board_nameStay details from the prebook
holder, guests[]As sent
price, cancellation, pay_at_property[]Commercial terms as prebooked
cancellation_resultFee and refund after cancellation
error{code, message} when failed or unknown
booked_byConsole user email, or api: plus the API key prefix
account_charge{status, amount, refunded, currency}; status is held, charged, released or refunded
amendments[]{at, type, status, detail} for change requests
events[]Timeline {at, type, detail, actor}: created, confirmed, failed, unknown, reconciled, refreshed, amended, cancelled

Errors

{ "error": { "code": "INVALID_REQUEST", "message": "request failed validation",
             "request_id": "req_…", "fields": [ { "field": "checkout", "message": "must be after checkin" } ] } }

Branch on code, never on message. retryable: true marks errors that are safe to retry with backoff.

HTTPcodeMeaning
400MALFORMED_JSON, IDEMPOTENCY_KEY_REQUIREDFix the request
401UNAUTHENTICATEDMissing, unknown or revoked API key
402INSUFFICIENT_CREDITNot enough available credit for the booking net
404NOT_FOUNDUnknown id, or one that belongs to another account
409SUPPLIER_REJECTED_REQUESTSupplier refused: rate gone, invalid offer, booking not confirmed
409PREBOOK_ALREADY_USED, NOT_CANCELLABLE, NOT_AMENDABLEState conflict
410PREBOOK_EXPIRED, SEARCH_EXPIREDPrebook or search again
413BODY_TOO_LARGEBody over 64 KB
415UNSUPPORTED_MEDIA_TYPESend Content-Type: application/json
422INVALID_REQUEST, NO_ELIGIBLE_SUPPLIERS, IDEMPOTENCY_KEY_REUSED, ACCOUNT_CURRENCY_MISMATCHSee fields[] or the message
502SUPPLIER_UNAVAILABLE, SUPPLIER_BAD_RESPONSE, SUPPLIER_AUTH_FAILEDSupplier-side problem; retry later or try another rate
503SUPPLIER_RATE_LIMITED, SUPPLIER_CIRCUIT_OPENBack off and retry
504SUPPLIER_TIMEOUTRetry reads; never blindly retry a booking

Go-live checklist

Changelog

DateChange
2026-10Hotels v1, additive: search sessions with filters, facets, sorting and paging (GET /hotels/v1/searches/{id}); destinations autocomplete; room types, policies and reviews; master-data content API; booking list filters; lead-guest amendment; cancellation quote; prepaid account with statement. New fields on search, rates and bookings. Existing fields unchanged.
2026-10Console-issued API keys; supplier distribution rules now apply to every request. No contract changes.
2026-10Hotels v1: search, content, prebook, idempotent booking, refresh and cancel.
2026-10Flights v1 search (beta).

Questions? developers@supplier.travel · Quote your X-Request-Id.