Travel To Discover

Loading documentation…

3. API Reference

Export .md

Book

POST /api/trade/book

Places a hotel booking order that reserves a room against a specific rate package.

This is the terminating call of the hotel booking flow. It creates a confirmed reservation with the supplier and covers pre-paid account deduction, order creation and the platform's reliability mechanisms.

Critical Prerequisites:

  • SessionId (REQUIRED in Header): must be supplied so the booking process can be tracked
  • Must be called after CheckAvail: guarantees the rate package is still available
  • JWT Authentication: an Authorization header carrying a Bearer token is required

Required Parameters:

  • customerReferenceNo: the booking's reference number
  • ratePkgId: the rate package id being booked
  • holder: holder details for the booking
  • guests: guest details for the booking
  • sessionId (in Header): session id used to track the booking process

Booking Flow Integration:

  1. Search: call HotelRates to retrieve available room options
  2. Select: the user picks one rate package
  3. Verify: call CheckAvail to re-confirm availability and price
  4. Book: call Book to place the final reservation - this step
  5. Confirm: query the order status to verify the booking succeeded

Idempotency:

  • Requests are idempotent on customerReferenceNo
  • A duplicate request carrying the same identifiers returns the already-created order
  • This is what prevents accidental double bookings
  • A request under a different customerReferenceNo whose content (hotel, dates, rooms and guests) matches a booking still active on your account is refused with 100001003: msg names that booking, and nothing is sent to the supplier. It stays refused for as long as that booking is active, however long you wait: find it with QueryOrders, or cancel it first

Enterprise Reliability Features:

  • Order status tracking and confirmation
  • Session management for booking context
  • Transaction rollback when a call fails

Response Features:

  • Confirmed booking details
  • Supplier reference numbers
  • Order tracking information

Error Handling:

  • Timeout errors when the upstream or supplier is slow
  • Reliability mechanisms covering booking failures
  • Order rollback on critical failures

Common Use Cases:

  • Final confirmation of a hotel reservation

Request headers

Header Required Description
Client-Request-Timestamp No Time the client began sending the request, as a unix timestamp.
Currency No Currency the rates should be returned in, as an ISO 4217 code (https://en.wikipedia.org/wiki/ISO_4217).
IP No Client IP address. Both IPv4 and IPv6 are accepted.
Language No IETF BCP 47 language tag (https://masonreview.com/posts/ietf-bcp-47-language-tags/), for example "en" or "en-US". Defaults to "en-US".
Market No Point-of-sale market for this call, as an ISO 3166-1 alpha-2 country code. Required only when your account carries a market restriction: an allow-list cannot admit a market you have not stated, so such an account refuses calls that omit it.
Request-Id No Identifies this single request. Must not be reused across requests.
Session-Id Yes Recommended on every client request, and mandatory throughout the booking flow.
Test No Test flags, expressed as key-value pairs such as "hotel=HC1&scenario=priceChange". When the server does not recognise the value, the call behaves exactly as if no Test header had been sent.
Timeout-Milliseconds No Client-specified timeout. The server makes a best effort to return data before it elapses.
Trace-Id No Correlates a group of related requests for tracing.
User-Ref No Your own identifier for the person on whose behalf this call is made. Required only once named users have been authorised on your account, after which every call must identify one and is authorised against that user's scopes as well as the key's.
Authorization Yes Must start with "Bearer ". The token comes from the Ticket response. The Ticket call itself does not need this header.

Request body

Field Type Required Description
customerReferenceNo string Yes Your own reference for this booking, and the idempotency key for the endpoint — retrying with the same value returns the original order instead of creating a second one. A UUID is the usual choice.
ratePkgId string Yes RatePkgId identifying the rate being booked. Obtained from a HotelList or HotelRates response, and only resolvable within the session that produced it — send that session's id in the Session-Id header.
holder Holder Yes Holder contains the booking contact information
holder.firstName string Yes
holder.lastName string Yes
holder.email string No
holder.phone Phone No
holder.phone.countryCode string No AE
holder.phone.countryNumber integer No 971
holder.phone.number string No 525757249
guests[] array Yes Guests contains the list of guests for this room
guests[].roomIndex integer Yes Assigned room index for this guest, starts from 1
guests[].firstName string Yes First name of this guest
guests[].lastName string Yes Last name of this guest
guests[].nationalityCode string No Nationality code of this guest
guests[].age integer No Age of this guest, only matters for children
guests[].isChild boolean No Indicates if this guest is a child, determined by clients themselves
guests[].gender enum No Optional. Sent to the supplier when their contract asks for it (requiredGuestFields includes gender). Matched case-insensitively, and M / F are accepted as shorthand, because a partner's PMS emits whatever it emits. Anything outside the listed values is REFUSED rather than mapped to UNSPECIFIED — guessing a traveller's gender to satisfy a supplier's contract is worse than telling you the value was not understood. Allowed: MALE, FEMALE, OTHER, UNSPECIFIED.
guests[].dateOfBirth string No Optional. ISO YYYY-MM-DD. Required only when the supplier's contract asks for it (requiredGuestFields includes dateOfBirth), and for a child or infant, where an age alone does not identify the birthday the hotel checks at the desk. age remains accepted in its place.
guests[].passportNumber string No Optional, max 50 characters. Required only when the supplier's contract asks for it (requiredGuestFields includes passportNumber). Encrypted at rest and never returned in full — order reads show the last four characters only.
callbackUrl string No Optional. Registers an outbound order-status webhook for this order: every later status change — and every amendment — is POSTed to this address as an OrderStatusWebhook, signed with your credential's webhook secret. Omit it and no callbacks are sent for the order. OrderStatusWebhook, not OrderStatusCallback: the outbound body carries a fourth event, order_modified, that the inbound /api/callback/order route does not accept, so the two directions have separate schemas. Size your handler's event enum on the outbound one. Must be https. That is a deliberate constraint rather than a transport preference — the delivery carries the signed order payload — and plain http, a private, loopback or shared (100.64.0.0/10) address, a host name that only exists on a private network (localhost, a single-label name, *.internal, *.local), or a non-URL is refused as 100000400 / ERR.PARAM naming callbackUrl. The field was accepted and dropped before a dispatcher existed, so no configuration that was ever actually delivered to is refused by this. A host name is checked again at delivery. It is resolved before every attempt, an attempt whose host resolves to a non-public address is not sent, and redirects are never followed.

Example request

json
{
  "customerReferenceNo": "9d1f4b2a-7c60-4f3e-9a58-1b0c2d3e4f50",
  "ratePkgId": "rp_8f3c1a7e6b2d40f9a51c",
  "holder": {
    "firstName": "John",
    "lastName": "Doe",
    "email": "john.doe@example.com",
    "phone": {
      "countryCode": "AE",
      "countryNumber": 971,
      "number": "525757249"
    }
  },
  "guests": [
    {
      "roomIndex": 1,
      "firstName": "John",
      "lastName": "Doe",
      "nationalityCode": "US",
      "isChild": false
    },
    {
      "roomIndex": 1,
      "firstName": "Jane",
      "lastName": "Doe",
      "nationalityCode": "US",
      "isChild": false
    }
  ]
}

Response

Successful calls return the standard envelope. code is 0 on success — check it rather than the HTTP status alone, because some business failures are delivered with HTTP 200.

Field Type Required Description
code integer No
msg string No
data hotel.trade.protocol.BookResp No
data.hotelOrder hotel.supplier.domain.HotelOrder No
data.hotelOrder.status OrderStatus Yes OrderStatus indicates the current status of the order Allowed: 0, 1, 2, 3, 4, 5.
data.hotelOrder.statusRemark string No
data.hotelOrder.checkIn string Yes CheckIn is the check-in date, defaults to current date
data.hotelOrder.checkOut string Yes CheckOut is the check-out date, defaults to 7 days from now
data.hotelOrder.nightCount integer No Number of nights for the stay
data.hotelOrder.roomCount integer No CheckOut is the check-out date for the booking
data.hotelOrder.bookingTime string Yes BookingTime is when the booking was created
data.hotelOrder.hotelConfirmNo string No HotelConfirmNo is the confirmation number from the hotel
data.hotelOrder.holder Holder Yes Holder contains the person who made the booking
data.hotelOrder.holder.firstName string Yes
data.hotelOrder.holder.lastName string Yes
data.hotelOrder.holder.email string No
data.hotelOrder.holder.phone Phone No
data.hotelOrder.platformReferenceNo string Yes PlatformReferenceNo is the internal platform order identifier
data.hotelOrder.customerReferenceNo string No CustomerReferenceNo is the unique order identifier from the customer
data.hotelOrder.commissionableRate Money No used for commission calculation
data.hotelOrder.commissionableRate.currency string Yes Currency code, e.g. "USD".
data.hotelOrder.commissionableRate.amount number Yes Monetary amount, e.g. "14.50".
data.hotelOrder.netRate Money Yes
data.hotelOrder.netRate.currency string Yes Currency code, e.g. "USD".
data.hotelOrder.netRate.amount number Yes Monetary amount, e.g. "14.50".
data.hotelOrder.grossRate Money No
data.hotelOrder.grossRate.currency string Yes Currency code, e.g. "USD".
data.hotelOrder.grossRate.amount number Yes Monetary amount, e.g. "14.50".
data.hotelOrder.respectGrossRate boolean No You should respect GrossRate if RespectGrossRate is true; default as false
data.hotelOrder.cancelTime string No CancelTime is when the booking was cancelled, if applicable
data.hotelOrder.cancelReason string No CancelReason contains the reason if the order was cancelled
data.hotelOrder.refundedPrice Money No RefundedPrice is the amount that has been refunded to the customer
data.hotelOrder.refundedPrice.currency string Yes Currency code, e.g. "USD".
data.hotelOrder.refundedPrice.amount number Yes Monetary amount, e.g. "14.50".
data.hotelOrder.supplier integer No
data.hotelOrder.supplierReferenceNo string Yes SupplierReferenceNo is the unique order identifier from the supplier // Supplier identifies which supplier processed this order
data.hotelOrder.hotel OrderHotelInfo No hotel-specific information
data.hotelOrder.hotel.hotelId string No
data.hotelOrder.hotel.destinationId string No index
data.hotelOrder.hotel.name I18N No hotel name
data.hotelOrder.hotel.rating number No Rating, hotel rating, range [0.0, 5.0]
data.hotelOrder.hotel.address I18N No Localised string set. Keys are fixed language codes.
data.hotelOrder.hotel.latlngCoordinator LatlngCoordinator No
data.hotelOrder.hotel.logoURL string No
data.hotelOrder.rooms[] array No detailed information for each booked room
data.hotelOrder.rooms[].roomTypeId string Yes standardized roomTypeId, e.g. "R001"
data.hotelOrder.rooms[].roomTypeName I18N Yes standardized room type name; if not recognized, we'll give a default name like "Standard Room"
data.hotelOrder.rooms[].hotelId string Yes hotelId, duplicate of parent HotelId for indicating identified room
data.hotelOrder.rooms[].rates[] array<hotel.supplier.domain.RoomRatePkg> No core structure, room offers under this room
data.hotelOrder.rooms[].ratePkgId string Yes used as key input for checkAvail & book APIs
data.hotelOrder.rooms[].refundableMode RefundableMode No Refundability of the rate package: full = RefundableModeFully - free cancellation partial = RefundableModePartially - partial cancellation no = RefundableModeNo - not refundable Allowed: full, partial, no.
data.hotelOrder.rooms[].refundableUntil string No RefundableMode is valid until RefundableUntil
data.hotelOrder.rooms[].cancelFees[] array<hotel.supplier.domain.ComputedCancelPolicyItem> No
data.hotelOrder.rooms[].originalRoomNaming OriginalRoomNaming No origin room naming fields from supplier
data.hotelOrder.rooms[].rate Rate Yes single room price for multiple nights
data.hotelOrder.rooms[].totalRate Rate Yes total price for multiple rooms (and multiple nights)
data.hotelOrder.rooms[].rateComment string No
data.hotelOrder.rooms[].includesPackaging boolean No Whether this is a packaged product (e.g., flight+hotel combo)
data.hotelOrder.rooms[].checkIn string Yes CheckIn is the check-in date, defaults to current date
data.hotelOrder.rooms[].checkOut string Yes CheckOut is the check-out date, defaults to 7 days from now
data.hotelOrder.rooms[].board Board No Board info - Standard meal plan following liteapi standard
data.hotelOrder.rooms[].tax Tax No Taxes and fees, generally collected by hotels on behalf of the government, are fixed costs and do not participate in the price increase during the distribution process. Therefore, they are listed separately.
data.hotelOrder.rooms[].roomIndex integer No room index
data.hotelOrder.rooms[].guests[] array No information about all guests in this room
data.hotelOrder.rooms[].refundInfo[] array<hotel.supplier.domain.OrderRoomRefundInfo> No
code integer No
msg string No

Example response

json
{
  "code": 0,
  "msg": "string",
  "data": {
    "hotelOrder": {
      "status": 1,
      "statusRemark": "aborted",
      "checkIn": "2026-01-01",
      "checkOut": "2026-01-03",
      "nightCount": 0,
      "roomCount": 0,
      "bookingTime": "2026-01-01T10:00:00Z",
      "hotelConfirmNo": "123456",
      "holder": {
        "email": "johndoe@example.com",
        "firstName": "John",
        "lastName": "Doe",
        "phone": {
          "countryCode": "1",
          "number": "1234567890"
        }
      },
      "platformReferenceNo": "1234567890",
      "customerReferenceNo": "1234567890",
      "commissionableRate": {
        "currency": "USD",
        "amount": 0
      },
      "netRate": {
        "currency": "USD",
        "amount": 0
      },
      "grossRate": {
        "currency": "USD",
        "amount": 0
      },
      "respectGrossRate": false,
      "cancelTime": "2026-01-02T10:00:00Z",
      "cancelReason": "Customer requested cancellation",
      "refundedPrice": {
        "amount": 99,
        "currency": "USD"
      },
      "supplier": 20000001,
      "supplierReferenceNo": "string",
      "hotel": {
        "hotelId": "string",
        "destinationId": "0192f3a7-5c41-7e88-b3d2-9a1c4e6f0b57",
        "name": {
          "en": "Jumeirah Beach Hotel"
        },
        "rating": 4.5,
        "address": {
          "en": "Millenium Al Barsha"
        },
        "latlngCoordinator": {
          "google": {},
          "gaode": {}
        },
        "logoURL": "https://cdn.example.com/hotel/461850557_1.jpg"
      },
      "rooms": [
        {
          "roomTypeId": "string",
          "roomTypeName": {},
          "hotelId": "string",
          "rates": [],
          "ratePkgId": "string",
          "refundableMode": "full",
          "refundableUntil": "2006-01-02T15:04:05Z07:00",
          "cancelFees": [],
          "originalRoomNaming": {},
          "rate": {},
          "totalRate": {},
          "rateComment": "string",
          "includesPackaging": false,
          "checkIn": "2026-01-01",
          "checkOut": "2026-01-03",
          "board": {},
          "tax": {},
          "roomIndex": 0,
          "guests": [],
          "refundInfo": []
        }
      ]
    }
  }
}

Failure responses

HTTP Meaning
400 Bad Request
401 Unauthorized
403 Forbidden
429 Too Many Requests
504 Upstream or supplier timeout

Every failure uses the { code, msg } envelope. See Error handling for the full business-code table.

Code samples

bash
# $SESSION_ID is the sessionId returned by hotelList (data.basic.sessionId).
curl -X POST https://api-test.ttdbooking.com/api/trade/book \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TICKET" \
  -H "Session-Id: $SESSION_ID" \
  -H "Request-Id: $(uuidgen)" \
  -d '{
    "customerReferenceNo": "9d1f4b2a-7c60-4f3e-9a58-1b0c2d3e4f50",
    "ratePkgId": "rp_8f3c1a7e6b2d40f9a51c",
    "holder": {
      "firstName": "John",
      "lastName": "Doe",
      "email": "john.doe@example.com",
      "phone": {
        "countryCode": "AE",
        "countryNumber": 971,
        "number": "525757249"
      }
    },
    "guests": [
      {
        "roomIndex": 1,
        "firstName": "John",
        "lastName": "Doe",
        "nationalityCode": "US",
        "isChild": false
      },
      {
        "roomIndex": 1,
        "firstName": "Jane",
        "lastName": "Doe",
        "nationalityCode": "US",
        "isChild": false
      }
    ]
  }'

Try it

Try it

POST /api/trade/book
A ticket will be fetched with ttdbooking_api_demo.
Session-IdRequired here. One will be opened with a hotelList search on the first request, then reused across these pages.