Booking
The Book API books a departure of a package for your travellers. It checks the rooms, travellers, seats and price, blocks the seats, and returns a provisional booking with a booking code. TravClan later confirms the booking and assigns a final booking code.
Request Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
code | String | Yes | Package ID, the same code used for Package Details, e.g. fdljkyw. |
Request Body Parameters
Send every field. Use [] when nothing was chosen.
| Parameter | Type | Required | Description |
|---|---|---|---|
departure_code | String | Yes | departures[].code of the selected departure. |
buyer_booking_reference | String | Yes | Your own unique reference for this booking. TravClan uses it to match the booking when sending the final booking code. |
occupancy | Array of Objects | Yes | One item per room. See occupancy[]. |
hotels | Array of Strings | Yes | hotels[].code of every included hotel stay. |
hotel_upgrades | Array of Objects | Yes | One item per chosen hotel upgrade. See hotel_upgrades[]. |
sightseeings | Array of Strings | Yes | sightseeings[].sightseeing.code of the included activities. |
sightseeing_addon_codes | Array of Strings | Yes | addons[].code of each optional activity chosen. |
transfers | Array of Strings | Yes | transfers[].transfer.code of the included transfers. |
transfer_addon_codes | Array of Strings | Yes | transfers[].addons[].code of each transfer upgrade chosen. |
flights | Array of Strings | Yes | flights[].code of the included flights. |
flight_origin_addon_codes | Array of Strings | Yes | flights[].outbound.origin_addons[].code of the start city chosen. |
total_amount | Number | Yes | The total price you calculate room by room, including upgrades and add-ons. It must match the current price. |
currency | String | Yes | departures[].currency of the selected departure, e.g. INR. |
occupancy[]
occupancy[]Add one item per room. Every room needs at least one adult and must fit the departure's max_occupancy (DOUBLE = 2 people, TRIPLE = 3; one extra infant is allowed with 2 or more adults). Send the age of every child and infant.
| Parameter | Type | Required | Description |
|---|---|---|---|
num_of_adults | Integer | Yes | Adults in the room. |
child_with_bed_ages | Array of Integers | Yes | Age of each child with a bed. |
child_without_bed_ages | Array of Integers | Yes | Age of each child without a bed. |
infant_ages | Array of Integers | Yes | Age of each infant. |
hotel_upgrades[]
hotel_upgrades[]Add one item for each hotel stay the traveller upgrades. Keep the included stay's code in hotels as well.
| Parameter | Type | Required | Description |
|---|---|---|---|
base_hotel.code | String | Yes | The included stay being replaced, hotels[].code. |
upgrade_hotel.code | String | Yes | The upgrade chosen, hotels[].upgrades[].code. |
Traveller details are not sent hereThe Book API does not take traveller names or documents. Send them to your ACM (account manager) with the same
buyer_booking_reference.
Sample Request
Two rooms with one add-on of each kind. The codes are placeholders; use the codes from the Package Details response.
curl --request POST 'https://holidays-volt-sandbox.travclan.com/api/v1/fixed-departure/listings/{code}/book' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'x-api-key: Bearer <API_KEY>' \
--data '{
"departure_code": "fddxxxx",
"buyer_booking_reference": "BUY-12345",
"occupancy": [
{
"num_of_adults": 2,
"child_with_bed_ages": [
8
],
"child_without_bed_ages": [],
"infant_ages": []
},
{
"num_of_adults": 1,
"child_with_bed_ages": [],
"child_without_bed_ages": [
4
],
"infant_ages": [
1
]
}
],
"hotels": [
"fhsstay1",
"fhsstay2"
],
"hotel_upgrades": [
{
"base_hotel": {
"code": "fhsstay1"
},
"upgrade_hotel": {
"code": "fhsupg1"
}
}
],
"sightseeings": [
"ssxxxx1",
"ssxxxx2"
],
"sightseeing_addon_codes": [
"fsaxxxx"
],
"transfers": [
"fdtxxxx1",
"fdtxxxx2"
],
"transfer_addon_codes": [
"ftaxxxx"
],
"flights": [
"flsxxxx1"
],
"flight_origin_addon_codes": [
"foaxxxx"
],
"total_amount": 136500,
"currency": "INR"
}'What Book Checks
Before booking, the API checks the request. If any check fails, no booking is made and no seats are blocked.
| Check | The booking fails when |
|---|---|
| Rooms | A room has no adult, or more people than max_occupancy allows. |
| Minimum adults | The adults across all rooms are fewer than min_occupancy. |
| Seats | Adults and children need more seats than total_available. Infants do not take a seat. |
| Price | total_amount does not match the current price for these travellers and selections. |
Response Body Structure
Root
| Key | Type | Description |
|---|---|---|
status | Integer | 200 when the booking is made. |
data | Object | The booking. See data. |
data
dataA 200 response means the seats are blocked and a provisional booking is created. Show "Booking received" with booking_code, your buyer_booking_reference, the trip dates and booking_amount. status is CONFIRMED (the booking is accepted) and internal_booking_status is PENDING until TravClan confirms the booking.
| Key | Type | Description |
|---|---|---|
booking_code | String | Provisional booking ID. Different from the final booking code assigned when TravClan confirms the booking. |
listing_code | String | Package ID. |
departure_code | String | Departure booked. |
buyer_booking_reference | String | Your reference, as sent. |
travel_start_date | String (YYYY-MM-DD) | First day of travel. |
travel_end_date | String (YYYY-MM-DD) | Last day of travel. |
booking_amount | Number | Amount booked, in currency. |
currency | String | Currency, e.g. INR. |
status | String | CONFIRMED means the booking was accepted and the seats are blocked. |
internal_booking_status | String | PENDING until TravClan confirms the booking. |
bms_booking_code | String or null | Booking code in Volt's booking system, once created. |
booking_remark | String | Remark on the booking. |
request_body | Object | The request as stored. See data.request_body. |
total_available | Integer | Seats left on the departure after this booking. |
listing | Object | The package booked, with the same keys as data in the Package Details API. |
Sample Response
{
"status": 200,
"data": {
"booking_code": "fdbjkzn",
"listing_code": "fdljkkv",
"departure_code": "fddjky3",
"buyer_booking_reference": "BUY-12345",
"travel_start_date": "2026-11-07",
"travel_end_date": "2026-11-11",
"booking_amount": 195000,
"currency": "INR",
"status": "CONFIRMED",
"internal_booking_status": "PENDING",
"bms_booking_code": null,
"booking_remark": "",
"request_body": {
"hotels": [
"fhsjkkv"
],
"flights": [
"flsjkkv"
],
"currency": "INR",
"occupancy": [
{
"infant_ages": [],
"num_of_adults": 2,
"child_with_bed_ages": [
8
],
"child_without_bed_ages": []
},
{
"infant_ages": [
1
],
"num_of_adults": 1,
"child_with_bed_ages": [],
"child_without_bed_ages": [
4
]
}
],
"transfers": [
"fdtjkkv",
"fdtjkyi"
],
"sightseeings": [
"ssgjza1",
"ssgjzai",
"ssgjzja",
"ssgjzjh"
],
"total_amount": 195000,
"departure_code": "fddjky3",
"hotel_upgrades": [],
"domesticChannelId": "travclan-demo-channel",
"transfer_addon_codes": [],
"internationalChannelId": "travclan-demo-channel",
"isHolidaysVoltMerchant": true,
"buyer_booking_reference": "BUY-12345",
"sightseeing_addon_codes": [],
"flight_origin_addon_codes": []
},
"total_available": 3,
"listing": {
"title": "5N Singapore Getaway | Ex-DEL",
"code": "fdljkkv",
"destination_code": "desjkyf",
"destination_name": "Singapore",
"description": "<p>Discover the best of…",
"nights_count": 5,
"rooms_count": 1,
"min_pax": 1,
"star_category": 4,
"payment_policy": "<p>30% payment at the tim…",
"cancellation_policy": "<div class=\"min-w-0 lg:…",
"inclusions": "<ul class=\"space-y-2.5\">…",
"exclusions": "<ul class=\"space-y-2.5\">…",
"marketing_prices": {
"adult": {
"hotel": 13541.11,
"flight": 25179.05,
"land": 7936.51
},
"child_with_bed": {
"hotel": 10155.83,
"flight": 18884.29,
"land": 5952.38
},
"child_without_bed": {
"hotel": 8463.19,
"flight": 15736.9,
"land": 4960.32
},
"infant": {
"hotel": 1692.64,
"flight": 3147.38,
"land": 992.06
}
},
"images": [
{
"url": "https://…/singapore-gateway.jpg",
"is_hero": true
}
],
"tags": [
{
"code": "tgjkkv",
"name": "Family Friendly"
}
],
"departures": [
{
"code": "fddjky3",
"source": "TRAVCLAN",
"travel_start_date": "2026-11-07",
"travel_end_date": "2026-11-11",
"booking_start_date": "2026-08-12",
"booking_end_date": "2026-10-31",
"total_inventory": 100,
"total_available": 3,
"total_booked": 97,
"adult_price": 40000,
"infant_price": 5000,
"child_price_with_bed": 30000,
"child_price_without_bed": 25000,
"single_adult_price": 55000,
"currency": "INR",
"max_age_child_with_bed": 9,
"max_age_child_without_bed": 9,
"max_age_infant": 2,
"min_occupancy": "DOUBLE",
"max_occupancy": "TRIPLE"
}
],
"hotels": [
{
"code": "fhsjkkv",
"hotel": {
"code": "htjrhf",
"name": "Holiday Inn Singapore A…",
"area_code": "arjkrw",
"area_name": "Singapore",
"providerReferenceId": 39648221
},
"meal_type": "BEDANDBREAKFAST",
"day_of_trip": 1,
"rooms": [
{
"rooms": [
{
"code": "rmjgqt",
"name": "Standard Room",
"meal_type": "BEDANDBREAKFAST",
"nights_count": 5,
"data_source": 1
}
]
}
],
"proofs": [
{
"image_url": "https://…/inn.png",
"b2b_price": 400,
"b2c_price": 600,
"source": "MAKEMYTRIP",
"remarks": "Hotel Price Comparison"
}
],
"addons": [
{
"code": "fhajkkv",
"type": "ROOM",
"description": "Single sofa cum bed",
"adult_price": 400,
"infant_price": 100,
"child_price_with_bed": 200,
"child_price_without_bed": 150
}
],
"upgrades": [
{
"code": "fhsjkkm",
"hotel": {
"code": "htjyuy",
"name": "One Farrer Hotel",
"area_code": "arjkrw",
"area_name": "Singapore",
"providerReferenceId": 39684174,
"additional_details": {
"images": [],
"description": "",
"yt_videos": [],
"reviews": null,
"hero_image": "https://…/47f1880.jpg"
}
},
"meal_type": "BEDANDBREAKFAST",
"day_of_trip": 1,
"rooms": [],
"proofs": [],
"addons": [],
"upgrade_adult_price": 500,
"upgrade_single_adult_price": 500,
"upgrade_infant_price": 100,
"upgrade_child_price_with_bed": 200,
"upgrade_child_price_without_bed": 100
}
]
}
],
"sightseeings": [
{
"sightseeing": {
"name": "Gardens by the Bay (New…",
"description": "",
"code": "ssgjzja",
"isPopular": false,
"duration": null,
"tags": [],
"coordinates": [],
"images": [
{
"isHero": true,
"url": "https://…/picture-9.png"
}
],
"reviews": null
},
"type": "SIC",
"day_of_trip": 1
}
],
"addons": [
{
"code": "fsajkk7",
"sightseeing": {
"name": "Marina Bay Sands SkyPar…",
"description": null,
"code": "ssgj0z8",
"isPopular": false,
"duration": null,
"tags": [],
"coordinates": [],
"images": [],
"reviews": null
},
"type": "PRIVATE",
"day_of_trip": 1,
"adult_price": 50,
"child_price": 40,
"infant_price": 20
}
],
"transfers": [
{
"transfer": {
"code": "fdtjkkv",
"origin": "HAN",
"origin_type": "AIRPORT",
"destination": "arjkr6",
"destination_type": "AREA",
"transfer_type": "PRIVATE",
"occupancy": 4,
"min_pax": 2,
"unit": "PER_CAB",
"routes": [
{
"origin": "HAN",
"origin_type": "AIRPORT",
"destination": "arjkr6",
"destination_type": "AREA",
"description": "Transfer from HAN Airp…",
"transfer_mode": {
"code": "ftmjkkm",
"name": "4 seater Car",
"type": "CAR",
"occupancy": 4
}
}
]
},
"addons": []
}
],
"flights": [
{
"code": "flsjkkv",
"is_primary": true,
"day_of_trip": 1,
"origin": "Delhi",
"destination": "Singapore",
"journey_type": "ROUND_TRIP",
"outbound": {
"origin": "HKT",
"destination": "BLR",
"is_refundable": false,
"stop_count": 0,
"fare_class": "PUBLISHED",
"segments": [
{
"baggage": {
"check_in": {
"value": 25,
"unit": "KG"
},
"cabin": {
"value": 7,
"unit": "KG"
}
},
"cabin_class": "ECONOMY",
"airline": {
"airline_code": "QP",
"airline_name": "Akasa Air",
"flight_number": "QP624",
"fare_class": "PUBLISHED",
"fare_basis": "PUBLISHED"
},
"origin": {
"airport_code": "HKT",
"airport_name": "Phuket Internat…",
"terminal_number": "2",
"city_code": "HKT",
"city_name": "Phuket",
"country_name": "Thailand",
"timezone": "Asia/Bangkok",
"departure_time": "17:00:00"
},
"destination": {
"airport_code": "BLR",
"airport_name": "Kempegowda Inte…",
"terminal_number": "2",
"city_code": "BLR",
"city_name": "Bangalore",
"country_name": "India",
"timezone": "Asia/Kolkata",
"arrival_time": "19:20:00"
},
"duration": 260,
"total_duration": 260,
"ground_time": 0,
"stop_over": false,
"stop_over_point": "",
"stop_over_duration": 0
}
],
"origin_addons": []
},
"inbound": {
"origin": "Phuket",
"destination": "Bombay",
"is_refundable": false,
"stop_count": 0,
"fare_class": "Economy",
"segments": [],
"origin_addons": []
},
"proofs": [
{
"image_url": "https://…/qw.png",
"b2b_price": 400,
"b2c_price": 600,
"source": "MAKEMYTRIP",
"remarks": "Flight Comparison"
}
]
}
],
"additional_details": {
"images": [],
"description": "",
"yt_videos": [],
"reviews": null,
"hero_image": "https://…/singapore-1.png"
}
}
}
}Errors
| Status | When |
|---|---|
400 | Invalid body, occupancy, components, inventory or currency. |
401 | Missing or invalid API key. |
404 | Listing not found, or not bookable for your organisation. |
409 | Prices have changed: total_amount does not match the current price. |
500 | Unexpected error. |
How to use: On a 409, data.expected_amount is the current price. Read the package again with the Package Details API, recalculate the total, and show the new price to the traveller before booking again.
{
"status": 409,
"message": "Prices have changed",
"data": {
"requested_amount": 80000.0,
"expected_amount": 85000.0,
"difference": 5000.0,
"currency": "INR"
}
}{
"success": false,
"code": 401,
"message": "Unauthorized"
}API Reference
Updated 4 days ago
