Skip to content

Hotel API Specification

Overview

The Hotel API provides endpoints for hotel management, room calendar configuration, accommodation search, and GDS hotel search integration.

Base Path: /hotel

Content Types: - Request: application/json - Response: application/json

Response Format

All endpoints return a TlinqApiResponse object:

{
  "apiStatus": {
    "errorCode": "OK",
    "errorMessage": "Success"
  },
  "apiData": { ... }
}

Date Format: All dates are returned in ISO 8601 format (yyyy-MM-dd'T'HH:mm:ss)


Hotel Management Endpoints

POST /hotel/listHotels

Lists hotels with optional filtering (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | name | string | No | Filter by hotel name (partial match) | | area | string | No | Filter by area | | shDes | string | No | Filter by short description |

Request Example:

{
  "session": "user-session-token",
  "name": "Hilton",
  "area": "Downtown Dubai"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "hotelId": 101,
      "name": "Hilton Dubai Downtown",
      "shortName": "Hilton Downtown",
      "shDes": "5-star luxury hotel in downtown Dubai",
      "hotelDesc": "Experience luxury at its finest...",
      "productId": 5001,
      "direct": true,
      "indirect": false,
      "country": "UAE",
      "stars": 5,
      "infant": 2,
      "child": 12,
      "chkIn": "14:00",
      "chkOut": "12:00",
      "fees": 50.00,
      "feesDesc": "Tourism fee per night",
      "terms": "Free cancellation up to 24 hours before check-in",
      "area": "Downtown Dubai",
      "city": "Dubai",
      "release": 3,
      "reservationContact": "reservations@hiltondubai.com"
    },
    {
      "hotelId": 102,
      "name": "Hilton Dubai Creek",
      "shortName": "Hilton Creek",
      "shDes": "5-star hotel overlooking Dubai Creek",
      "productId": 5002,
      "direct": true,
      "indirect": false,
      "country": "UAE",
      "stars": 5,
      "area": "Dubai Creek",
      "city": "Dubai"
    }
  ]
}


POST /hotel/hotelLookup

Searches for hotels by name for autocomplete (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | name | string | No | Partial hotel name |

Request Example:

{
  "session": "user-session-token",
  "name": "Marriott"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    ["Marriott Downtown Dubai", 201],
    ["Marriott Al Jaddaf", 202],
    ["JW Marriott Marquis", 203]
  ]
}


POST /hotel/getHotel

Retrieves a hotel by ID (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | id | integer | Yes | Hotel ID |

Request Example:

{
  "session": "user-session-token",
  "id": 101
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "hotelId": 101,
    "name": "Hilton Dubai Downtown",
    "shortName": "Hilton Downtown",
    "shDes": "5-star luxury hotel in downtown Dubai",
    "hotelDesc": "Experience luxury at its finest with stunning views of Burj Khalifa...",
    "productId": 5001,
    "direct": true,
    "indirect": false,
    "country": "UAE",
    "stars": 5,
    "infant": 2,
    "child": 12,
    "chkIn": "14:00",
    "chkOut": "12:00",
    "fees": 50.00,
    "feesDesc": "Tourism fee per night",
    "terms": "Free cancellation up to 24 hours before check-in",
    "area": "Downtown Dubai",
    "city": "Dubai",
    "release": 3,
    "reservationContact": "reservations@hiltondubai.com"
  }
}


POST /hotel/saveHotel

Creates or updates a hotel (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotel | object | Yes | Hotel data object |

Request Example:

{
  "session": "user-session-token",
  "hotel": {
    "hotelId": null,
    "name": "New Luxury Resort",
    "shortName": "Luxury Resort",
    "shDes": "New 5-star beachfront resort",
    "country": "UAE",
    "stars": 5,
    "infant": 2,
    "child": 12,
    "chkIn": "15:00",
    "chkOut": "11:00",
    "area": "Palm Jumeirah",
    "city": "Dubai",
    "direct": true
  }
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "hotelId": 301,
    "name": "New Luxury Resort",
    "shortName": "Luxury Resort",
    "shDes": "New 5-star beachfront resort",
    "productId": 6001,
    "country": "UAE",
    "stars": 5,
    "infant": 2,
    "child": 12,
    "chkIn": "15:00",
    "chkOut": "11:00",
    "area": "Palm Jumeirah",
    "city": "Dubai",
    "direct": true,
    "indirect": false
  }
}


POST /hotel/listHotelRooms

Lists rooms for a hotel (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelId | integer | Yes | Hotel ID |

Request Example:

{
  "session": "user-session-token",
  "hotelId": 101
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "roomId": 1001,
      "hotelId": 101,
      "name": "Deluxe Room",
      "roomDesc": "Spacious room with city view",
      "maxOccupancy": 3,
      "maxAdults": 2,
      "extraBed": true,
      "bedding": "King or Twin",
      "extraBedRequired": false,
      "notes": null
    },
    {
      "roomId": 1002,
      "hotelId": 101,
      "name": "Executive Suite",
      "roomDesc": "Luxury suite with separate living area",
      "maxOccupancy": 4,
      "maxAdults": 2,
      "extraBed": true,
      "bedding": "King",
      "extraBedRequired": false,
      "notes": "Includes lounge access"
    }
  ]
}


POST /hotel/saveRoom

Creates or updates a hotel room (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | room | object | Yes | Room data object |

Request Example:

{
  "session": "user-session-token",
  "room": {
    "roomId": null,
    "hotelId": 101,
    "name": "Premium Suite",
    "roomDesc": "Luxurious suite with panoramic views",
    "maxOccupancy": 4,
    "maxAdults": 3,
    "extraBed": true,
    "bedding": "King",
    "extraBedRequired": false
  }
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "roomId": 1003,
    "hotelId": 101,
    "name": "Premium Suite",
    "roomDesc": "Luxurious suite with panoramic views",
    "maxOccupancy": 4,
    "maxAdults": 3,
    "extraBed": true,
    "bedding": "King",
    "extraBedRequired": false
  }
}


Calendar & Pricing Endpoints

POST /hotel/listRoomCalendar

Lists room calendar entries (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | roomId | integer | No | Room ID | | hotelId | integer | No | Hotel ID | | fromDate | string | Yes | Start date (yyyy-MM-dd) | | toDate | string | Yes | End date (yyyy-MM-dd) | | market | string | No | Market code (default: UAE) |

*Either roomId or hotelId must be provided.

Request Example:

{
  "session": "user-session-token",
  "roomId": 1001,
  "fromDate": "2025-07-01",
  "toDate": "2025-07-31",
  "market": "UAE"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "calendarEntryId": 50001,
      "roomId": 1001,
      "market": "UAE",
      "vendorId": null,
      "promo": null,
      "bookFrom": "2025-01-01T00:00:00",
      "bookTo": "2025-12-31T23:59:59",
      "stayDate": "2025-07-01T00:00:00",
      "baseRate": 500.00,
      "specDayRate": 600.00,
      "specialDay": false,
      "sdRateType": "ABS",
      "adultRate": 50.00,
      "adultRateType": "ABS",
      "childRate": 25.00,
      "childRateType": "ABS",
      "extraBedRate": 100.00,
      "rateBase": "ROOM",
      "mealBase": 1,
      "mealSupplements": "BB:30,HB:60,FB:90",
      "stopSale": false,
      "available": true,
      "onRequest": false,
      "mlos": 1,
      "mlosDesc": "Minimum 1 night",
      "sdmlos": 2,
      "notes": null,
      "adultSdRate": 60.00,
      "childSdRate": 30.00
    },
    {
      "calendarEntryId": 50002,
      "roomId": 1001,
      "market": "UAE",
      "stayDate": "2025-07-02T00:00:00",
      "baseRate": 500.00,
      "specialDay": false,
      "available": true
    }
  ]
}


POST /hotel/createRoomCalendar

Creates calendar entries for a room (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | template | object | Yes | Calendar entry template | | stayTo | string | Yes | End date for calendar | | specDays | string | Yes | Special day settings (comma-separated weekdays) |

Request Example:

{
  "session": "user-session-token",
  "template": {
    "roomId": 1001,
    "market": "UAE",
    "stayDate": "2025-08-01",
    "bookFrom": "2025-01-01",
    "bookTo": "2025-12-31",
    "baseRate": 450.00,
    "specDayRate": 550.00,
    "sdRateType": "ABS",
    "adultRate": 50.00,
    "adultRateType": "ABS",
    "childRate": 25.00,
    "childRateType": "ABS",
    "extraBedRate": 100.00,
    "rateBase": "ROOM",
    "mealBase": 1,
    "available": true,
    "stopSale": false,
    "mlos": 1
  },
  "stayTo": "2025-08-31",
  "specDays": "Fri,Sat"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "calendarEntryId": 60001,
      "roomId": 1001,
      "stayDate": "2025-08-01T00:00:00",
      "baseRate": 450.00,
      "specialDay": true,
      "available": true
    },
    {
      "calendarEntryId": 60002,
      "roomId": 1001,
      "stayDate": "2025-08-02T00:00:00",
      "baseRate": 450.00,
      "specialDay": true,
      "available": true
    }
  ]
}


POST /hotel/copyRoomCalendar

Copies calendar from one room to another (employees only).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | fromRoom | integer | Yes | Source room ID | | toRoom | integer | Yes | Target room ID | | market | string | Yes | Market code | | fromDate | string | Yes | Start date | | toDate | string | Yes | End date |

Request Example:

{
  "session": "user-session-token",
  "fromRoom": 1001,
  "toRoom": 1002,
  "market": "UAE",
  "fromDate": "2025-07-01",
  "toDate": "2025-07-31"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "recordsCopied": 31
  }
}


Meal Plan Endpoints

POST /hotel/listMealPlans

Lists meal plans.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelId | integer | No | Hotel ID (for hotel-specific plans) |

Request Example:

{
  "session": "user-session-token",
  "hotelId": 101
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "planId": 1,
      "plan": "RO",
      "name": "Room Only",
      "planDesc": "Accommodation without meals",
      "planOrder": 1,
      "adultCost": 0.00,
      "childCost": 0.00
    },
    {
      "planId": 2,
      "plan": "BB",
      "name": "Bed & Breakfast",
      "planDesc": "Accommodation with breakfast",
      "planOrder": 2,
      "adultCost": 30.00,
      "childCost": 15.00
    },
    {
      "planId": 3,
      "plan": "HB",
      "name": "Half Board",
      "planDesc": "Breakfast and dinner included",
      "planOrder": 3,
      "adultCost": 60.00,
      "childCost": 30.00
    },
    {
      "planId": 4,
      "plan": "FB",
      "name": "Full Board",
      "planDesc": "All meals included",
      "planOrder": 4,
      "adultCost": 90.00,
      "childCost": 45.00
    },
    {
      "planId": 5,
      "plan": "AI",
      "name": "All Inclusive",
      "planDesc": "All meals and beverages included",
      "planOrder": 5,
      "adultCost": 150.00,
      "childCost": 75.00
    }
  ]
}


Search Endpoints

POST /hotel/searchAccommodation

Searches for available accommodation.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | checkin | string | Yes | Check-in date (dd/MM/yyyy) | | checkout | string | Yes | Check-out date (dd/MM/yyyy) | | adults | integer | Yes | Number of adults | | children | integer | Yes | Number of children | | meals | string | No | Meal plan filter | | area | string | No | Area filter | | market | string | No | Market code | | maxBudget | number | No | Maximum budget | | extraBed | boolean | No | Include extra bed | | hotelId | integer | No | Specific hotel ID | | roomId | integer | No | Specific room ID | | context | string | No | "config" for employee view |

Request Example:

{
  "session": "user-session-token",
  "checkin": "15/07/2025",
  "checkout": "20/07/2025",
  "adults": 2,
  "children": 1,
  "meals": "BB",
  "area": "Downtown Dubai",
  "market": "UAE",
  "maxBudget": 5000
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "input": {
      "checkin": "2025-07-15T00:00:00",
      "checkout": "2025-07-20T00:00:00",
      "adults": 2,
      "children": 1,
      "market": "UAE",
      "area": "Downtown Dubai",
      "meals": "BB",
      "nights": 5
    },
    "results": [
      {
        "hotel": {
          "hotelId": 101,
          "name": "Hilton Dubai Downtown",
          "shortName": "Hilton Downtown",
          "stars": 5,
          "area": "Downtown Dubai",
          "city": "Dubai",
          "chkIn": "14:00",
          "chkOut": "12:00"
        },
        "resultSource": "DIRECT",
        "rooms": [
          {
            "roomId": 1001,
            "roomName": "Deluxe Room",
            "roomDesc": "Spacious room with city view",
            "maxOccupancy": 3,
            "mealPlan": "BB",
            "totalPrice": 3250.00,
            "pricePerNight": 650.00,
            "currency": "AED",
            "available": true,
            "onRequest": false
          },
          {
            "roomId": 1002,
            "roomName": "Executive Suite",
            "roomDesc": "Luxury suite with separate living area",
            "maxOccupancy": 4,
            "mealPlan": "BB",
            "totalPrice": 4500.00,
            "pricePerNight": 900.00,
            "currency": "AED",
            "available": true,
            "onRequest": false
          }
        ]
      }
    ]
  }
}


Online Hotel Search and Booking

These endpoints support dual-mode routing via the searchMode parameter: - searchMode="online" (default) -- Routes through the OnlineHotelSupplierRegistry to the active online hotel supplier (currently GoGlobal). - searchMode="contracted" -- Routes to the NTS/EntityFacade for contracted hotel inventory.

POST /hotel/searchCountries

Searches GoGlobal static data for countries matching a keyword. Used for the country autocomplete in the hotel search form.

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | keyword | string | Yes | Country name search keyword (minimum 3 characters recommended) |

Request Example:

{
  "session": "user-session-token",
  "keyword": "Uni"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    { "countryId": 123, "countryName": "United Arab Emirates", "countryCode": "AE" },
    { "countryId": 456, "countryName": "United Kingdom", "countryCode": "GB" },
    { "countryId": 789, "countryName": "United States", "countryCode": "US" }
  ]
}

Notes: - Queries the goglobal.country table (populated by StaticDataRefresher). - Case-insensitive partial match on countryName. - Returns up to 20 results, ordered by country name. - If the GoGlobal static data tables are empty, no results will be returned.


POST /hotel/getCities

Returns all cities for a given GoGlobal country ID. Used to populate the city dropdown after country selection.

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | countryId | long | Yes | GoGlobal country ID (from searchCountries response) |

Request Example:

{
  "session": "user-session-token",
  "countryId": 123
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    { "cityId": 1133, "cityName": "Abu Dhabi", "cityCode": "1133" },
    { "cityId": 1218, "cityName": "Dubai", "cityCode": "1218" },
    { "cityId": 1406, "cityName": "Sharjah", "cityCode": "1406" }
  ]
}

Notes: - Queries the goglobal.city table (populated by StaticDataRefresher). - The cityCode value is the GoGlobal city ID (same as cityId), which is used as the cityCode parameter in searchOffers. - Results are ordered by city name.


POST /hotel/searchOffers

Searches hotel offers via the configured online supplier (GoGlobal) or contracted hotel inventory.

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | searchMode | string | No | "online" (default) or "contracted" | | hotelIds | string | Conditional | Comma-separated hotel IDs (required for contracted mode) | | cityCode | string | Conditional | GoGlobal city ID (required for online mode when hotelIds not provided). Obtained from getCities response. | | checkInDate | string | Yes | Check-in date (yyyy-MM-dd) | | checkOutDate | string | No | Check-out date (yyyy-MM-dd) | | adults | integer | Yes | Number of adults | | children | integer | No | Number of children (default: 0) | | roomQuantity | integer | No | Number of rooms (default: 1) | | nationality | string | No | 2-letter country code for pricing nationality (default: "AE"). Mandatory for GoGlobal API — defaults to AE when not provided. | | currency | string | No | Currency code (default: configured default) | | paymentPolicy | string | No | Payment policy filter | | boardType | string | No | Meal plan filter (BB, HB, FB, AI, RO) | | bestRateOnly | boolean | No | Return best rate only | | exactDestination | boolean | No | When true, restrict results to the exact city (no nearby cities). GoGlobal default includes nearby cities. | | sortOrder | integer | No | 1=Price ASC, 2=Price DESC, 3=Name ASC |

Request Example (Online Mode):

{
  "session": "user-session-token",
  "searchMode": "online",
  "cityCode": "1234",
  "checkInDate": "2026-03-15",
  "checkOutDate": "2026-03-18",
  "adults": 2,
  "roomQuantity": 1,
  "currency": "USD"
}

Response Structure (Online Mode):

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "searchId": "HSRCH-20260315-123456",
    "pageId": 1,
    "totalRes": 25,  // total unique hotels (not individual offers)
    "offers": [
      {
        "offerId": "HSC-2026031500001",
        "hotelId": "56789",
        "hotelName": "Grand Hotel Dubai",
        "cityCode": "1234",
        "roomType": "BB",
        "roomCategory": "5",
        "roomDescription": "DOUBLE OCEAN VIEW",
        "boardType": "Bed & Breakfast",
        "currencyCode": "USD",
        "totalAmount": 450.00,
        "available": true,
        "checkInDate": "2026-03-15",
        "checkOutDate": "2026-03-18",
        "cancellationType": "REFUNDABLE",
        "cancellationDeadline": "2026-03-13T23:59:59"
      }
    ]
  }
}

Notes: - In online mode, the offerId field contains the GoGlobal HotelSearchCode, which is required for subsequent booking journey operations (valuateOffer, getPriceBreakdown, createBooking). - The HotelSearchCode is valid for approximately 20 minutes. - Online mode uses the OnlineHotelSupplierRegistry to resolve the active supplier (GoGlobal). - Contracted mode uses the EntityFacade with the HotelOffer entity configuration. - Result grouping: Results are grouped by hotel. The totalRes field represents the total number of unique hotels (not individual room offers). Each hotel may have multiple room offers in the offers array. - Page size: Configurable via goglobal.hotel.search.pageSize in goglobal-client.xml (default: 20 hotels per page). Use fetchOfferResults to retrieve subsequent pages.

Error Codes: - NORESULTS -- No hotel availability found - INVALID_FORMAT -- Date format incorrect - INVALID_PARAM -- Invalid city code or parameter - GENERAL -- Unexpected server error


POST /hotel/fetchOfferResults

Retrieves the next page of hotel search results from a previously executed search. Results are paginated by hotel — each page returns N hotels with all their room offers.

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | searchId | string | Yes | Search ID returned from searchOffers | | page | integer | Yes | Page number to retrieve (1-indexed) |

Request Example:

{
  "session": "user-session-token",
  "searchId": "HSRCH-20260315-123456",
  "page": 2
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "searchId": "HSRCH-20260315-123456",
    "pageId": 2,
    "totalRes": 45,
    "offers": [
      {
        "offerId": "HSC-2026031500021",
        "hotelId": "67890",
        "hotelName": "Marina Hotel Dubai",
        "cityCode": "1234",
        "roomType": "BB",
        "roomCategory": "4",
        "roomDescription": "TWIN STANDARD",
        "boardType": "Bed & Breakfast",
        "currencyCode": "USD",
        "totalAmount": 320.00,
        "available": true,
        "checkInDate": "2026-03-15",
        "checkOutDate": "2026-03-18",
        "cancellationType": "REFUNDABLE",
        "cancellationDeadline": "2026-03-13T23:59:59"
      }
    ]
  }
}

Notes: - The totalRes field represents the total number of unique hotels across all pages. - Page size is configurable via goglobal.hotel.search.pageSize in goglobal-client.xml (default: 20). - The offers array contains all room offers for the hotels on the requested page. Multiple offers may share the same hotelId (different room types for the same hotel). - Search results expire 30 minutes after the initial search. - Returns empty offers array if the page number exceeds available results.

Error Codes: - GENERAL -- Unexpected server error


POST /hotel/searchByName

Searches hotels by name (autocomplete).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | keyword | string | Yes | Search keyword | | countryCode | string | No | Country code filter | | subType | string | No | HOTEL_GDS or HOTEL_LEISURE | | max | integer | No | Maximum results |

Request Example:

{
  "session": "user-session-token",
  "keyword": "Marriott Dubai",
  "countryCode": "AE",
  "max": 10
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "hotelId": "MARDXB001",
      "name": "JW Marriott Marquis Dubai",
      "cityCode": "DXB",
      "address": "Sheikh Zayed Road, Business Bay",
      "rating": 5,
      "subType": "HOTEL_GDS"
    },
    {
      "hotelId": "MARDXB002",
      "name": "Marriott Downtown Dubai",
      "cityCode": "DXB",
      "address": "Downtown Dubai",
      "rating": 5,
      "subType": "HOTEL_GDS"
    }
  ]
}


POST /hotel/getByCity

Gets hotels by city code.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | cityCode | string | Yes | IATA city code | | radius | integer | No | Search radius | | ratings | string | No | Comma-separated star ratings |

Request Example:

{
  "session": "user-session-token",
  "cityCode": "DXB",
  "radius": 10,
  "ratings": "4,5"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "hotelId": "HTLDXB001",
      "name": "Burj Al Arab",
      "cityCode": "DXB",
      "address": "Jumeirah Beach Road",
      "rating": 5,
      "distance": 2.5
    },
    {
      "hotelId": "HTLDXB002",
      "name": "Atlantis The Palm",
      "cityCode": "DXB",
      "address": "Crescent Road, The Palm",
      "rating": 5,
      "distance": 8.0
    }
  ]
}


Online Hotel Booking Journey

The following endpoints implement the hotel booking journey through the online supplier (GoGlobal). They must be called in sequence after an availability search (/hotel/searchOffers), using the HotelSearchCode (returned as offerId in the search results) to identify the selected offer.

Booking Journey Flow: 1. /hotel/searchOffers -- Get available offers (returns offerId / HotelSearchCode), grouped by hotel 2. /hotel/fetchOfferResults -- (Optional) Paginate through additional results 3. /hotel/getHotel (with searchMode="online") -- Get hotel details 4. /hotel/valuateOffer -- Validate pricing and cancellation terms 5. /hotel/getPriceBreakdown -- Get nightly price breakdown 6. /hotel/createBooking -- Create the booking

POST /hotel/getHotel (Online Mode)

Retrieves detailed hotel information from the online supplier. When searchMode="online" is specified, the request is routed through the OnlineHotelSupplierRegistry to GoGlobal Operation 61 (Hotel Info Request).

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelId | string | Yes | GoGlobal hotel ID (from search results hotelId field) | | searchMode | string | No | "online" to use GoGlobal supplier; omit for contracted |

Request Example:

{
  "session": "user-session-token",
  "hotelId": "56789",
  "searchMode": "online"
}

Response Structure (CHotelInfo):

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "hotelId": "56789",
    "hotelName": "Grand Hotel Dubai",
    "description": "Luxury 5-star hotel with panoramic views of Burj Khalifa...",
    "address": "Sheikh Zayed Road, Downtown Dubai",
    "category": "5",
    "cityCode": "1218",
    "cityName": "Dubai",
    "countryCode": "AE",
    "facilities": "[{\"name\":\"Swimming Pool\"},{\"name\":\"Spa\"},{\"name\":\"Gym\"}]",
    "pictures": "[{\"url\":\"https://images.goglobal.travel/hotels/56789/1.jpg\"}]",
    "latitude": 25.2048,
    "longitude": 55.2708
  }
}

Notes: - Without searchMode="online", this endpoint retrieves hotel data from the contracted hotel database (NTS) using the id (integer) parameter instead. - The GoGlobal hotel ID is obtained from the hotelId field in the search results. - The category field contains the star rating as a string (e.g., "5"). - facilities and pictures are JSON strings; the frontend should parse them. - Hotel info includes geo-coordinates for map display.

Error Codes: - NOTFOUND -- Hotel ID not found in GoGlobal database - GENERAL -- Unexpected server error


POST /hotel/valuateOffer

Validates the current price and cancellation terms for a selected hotel offer. This is GoGlobal Operation 9 (Booking Valuation Request). The valuation confirms whether the price has changed since the availability search and returns detailed cancellation policies.

Permissions: agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelSearchCode | string | Yes | The offerId / HotelSearchCode from search results | | arrivalDate | string | Yes | Check-in date (yyyy-MM-dd). Required by GoGlobal API v3.16.8. | | nationality | string | No | 2-letter country code for pricing nationality. Some hotels have nationality-based pricing. |

Request Example:

{
  "session": "user-session-token",
  "hotelSearchCode": "HSC-2026031500001",
  "arrivalDate": "2026-03-15",
  "nationality": "AE"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "offerId": "HSC-2026031500001",
    "checkInDate": "2026-03-15",
    "totalAmount": 450.00,
    "currencyCode": "USD",
    "totalTax": 45.00,
    "cancellationDeadline": "2026-03-13T23:59:59",
    "cancellationType": "REFUNDABLE",
    "remarks": "Check-in from 14:00. Late checkout subject to availability.",
    "cancellationPolicies": [
      {
        "id": 1,
        "starting": "2026-03-13T00:00:00",
        "basedOn": "BOOKINGPRICE",
        "mode": "PCT",
        "value": 100
      }
    ]
  }
}

Response Fields: | Field | Type | Description | |-------|------|-------------| | offerId | string | The HotelSearchCode (unchanged) | | checkInDate | string | Confirmed arrival date | | totalAmount | number | Confirmed total price (may differ from search) | | currencyCode | string | Currency code | | totalTax | number | Tax amount included in total | | cancellationDeadline | string | Free cancellation deadline (ISO 8601) | | cancellationType | string | REFUNDABLE or NON_REFUNDABLE | | remarks | string | Hotel-specific remarks and conditions | | cancellationPolicies | array | Detailed cancellation policy rules |

Cancellation Policy Fields: | Field | Type | Description | |-------|------|-------------| | id | integer | Policy sequence ID | | starting | string | Date/time when policy takes effect | | basedOn | string | What the penalty is based on (BOOKINGPRICE, FIRSTNIGHT) | | mode | string | Penalty calculation mode (PCT = percentage, ABS = fixed amount) | | value | number | Penalty value (percentage or amount depending on mode) |

Error Codes: - EXPIRED -- Offer expired (HotelSearchCode no longer valid, re-search required) - GENERAL -- Unexpected server error


POST /hotel/getPriceBreakdown

Retrieves the nightly price breakdown per room for a selected offer. This is GoGlobal Operation 14 (Price Breakdown Request). Provides granular pricing detail for each night of the stay.

Permissions: guest, agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelSearchCode | string | Yes | The offerId / HotelSearchCode from search results |

Request Example:

{
  "session": "user-session-token",
  "hotelSearchCode": "HSC-2026031500001"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "hotelSearchCode": "HSC-2026031500001",
    "rooms": [
      {
        "roomId": 1,
        "nights": [
          { "date": "2026-03-15", "price": 150.00, "currency": "USD" },
          { "date": "2026-03-16", "price": 150.00, "currency": "USD" },
          { "date": "2026-03-17", "price": 150.00, "currency": "USD" }
        ]
      }
    ]
  }
}

Response Fields: | Field | Type | Description | |-------|------|-------------| | hotelSearchCode | string | The HotelSearchCode | | rooms | array | Array of room breakdown objects | | rooms[].roomId | integer | Room sequence number | | rooms[].nights | array | Array of nightly rate objects | | rooms[].nights[].date | string | Night date (yyyy-MM-dd) | | rooms[].nights[].price | number | Price for that night | | rooms[].nights[].currency | string | Currency code |

Notes: - For multi-room bookings, each room has its own breakdown. - Nightly prices may vary (e.g., weekend surcharges). - The sum of nightly prices per room may differ slightly from the total due to rounding.

Error Codes: - EXPIRED -- Offer expired (HotelSearchCode no longer valid) - GENERAL -- Unexpected server error


POST /hotel/createBooking

Creates a hotel booking for a validated offer. This is GoGlobal Operation 2 (Booking Insert Request). The offer should be valuated first via /hotel/valuateOffer to confirm pricing.

Permissions: agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelSearchCode | string | Yes | The offerId / HotelSearchCode from search results | | clientRef | string | No | Agency booking reference (e.g., TQ-BK-2026-0001) | | title | string | Yes | Lead guest title (MR., MRS., MS., MISS) | | firstName | string | Yes | Lead guest first name | | lastName | string | Yes | Lead guest last name | | email | string | Yes | Contact email address | | phone | string | No | Contact phone number | | nationality | string | No | 2-letter guest nationality code (e.g. "AE", "GB") | | remarks | string | No | Booking remarks / special requests | | roomGuests | string | No | JSON array of per-room guest arrays for multi-room bookings (see below) |

roomGuests format (JSON string):

[
  [
    { "personId": 1, "title": "MR.", "firstName": "John", "lastName": "Smith" },
    { "personId": 2, "title": "MRS.", "firstName": "Jane", "lastName": "Smith" }
  ],
  [
    { "personId": 3, "title": "MR.", "firstName": "Bob", "lastName": "Jones", "age": 10 }
  ]
]
Each inner array represents one room. Each guest has personId (sequential), title, firstName, lastName, and optional age (for children).

Request Example:

{
  "session": "user-session-token",
  "hotelSearchCode": "HSC-2026031500001",
  "clientRef": "TQ-BK-2026-0001",
  "title": "MR.",
  "firstName": "John",
  "lastName": "Smith",
  "email": "john.smith@example.com",
  "phone": "+44123456789",
  "nationality": "GB",
  "remarks": "Late arrival, estimated 22:00"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "goBookingCode": "GG-123456",
    "goReference": "REF-789012",
    "bookingStatus": "C",
    "totalPrice": 450.00,
    "currency": "USD",
    "hotelId": "56789",
    "hotelName": "Grand Hotel Dubai",
    "hotelConfirmation": "HC-GRAND-2026-001",
    "clientBookingCode": "TQ-BK-2026-0001"
  }
}

Response Fields: | Field | Type | Description | |-------|------|-------------| | goBookingCode | string | GoGlobal booking code (permanent, used for all subsequent operations) | | goReference | string | GoGlobal supplier reference number | | bookingStatus | string | Booking status code (see below) | | totalPrice | number | Confirmed booking price | | currency | string | Currency code | | hotelId | string | GoGlobal hotel ID | | hotelName | string | Hotel name | | hotelConfirmation | string | Hotel's own confirmation number (may be null for RQ bookings) | | clientBookingCode | string | Agency reference code (echoed from request) |

Booking Status Codes: | Code | Description | |------|-------------| | C | Confirmed -- booking is confirmed by the hotel | | RQ | On Request -- hotel confirmation pending (poll via booking status endpoint) | | RJ | Rejected -- booking was rejected by the hotel |

Notes: - A status of C means the booking is immediately confirmed. - A status of RQ means the hotel needs to confirm; use the booking status endpoint to poll for resolution. - The goBookingCode is the permanent identifier for cancellation, status check, voucher generation, and amendment operations. - Room configuration at booking time must match the configuration used during the availability search.

Error Codes: - EXPIRED -- Offer expired (HotelSearchCode no longer valid, re-search required) - INVALID_PARAM -- Invalid passenger data or room mismatch - GENERAL -- Unexpected server error


Data Models

CHotel

Field Type Description
hotelId integer Unique hotel identifier
name string Hotel name
shortName string Short hotel name
shDes string Short description
hotelDesc string Full description
productId integer Associated product ID
direct boolean Direct contract
indirect boolean Indirect contract
country string Country code
stars integer Star rating
infant integer Infant age limit
child integer Child age limit
chkIn string Check-in time
chkOut string Check-out time
fees number Additional fees
feesDesc string Fee description
terms string Terms and conditions
area string Hotel area
city string City name
release integer Release days
reservationContact string Reservation contact

CHotelRoom

Field Type Description
roomId integer Room identifier
hotelId integer Hotel ID
name string Room name/type
roomDesc string Room description
maxOccupancy integer Maximum guests
maxAdults integer Maximum adults
extraBed boolean Extra bed allowed
bedding string Bedding type
extraBedRequired boolean Extra bed required
notes string Room notes

CRoomCalendarEntry

Field Type Description
calendarEntryId integer Entry identifier
roomId integer Room ID
market string Market code
vendorId integer Vendor ID
promo string Promotion code
bookFrom datetime Booking window start
bookTo datetime Booking window end
stayDate datetime Stay date
baseRate number Base room rate
specDayRate number Special day rate
specialDay boolean Is special day
sdRateType string Special day rate type (ABS, PCT)
adultRate number Extra adult rate
adultRateType string Adult rate type
childRate number Child rate
childRateType string Child rate type
extraBedRate number Extra bed rate
rateBase string Rate basis (ROOM, PAX)
mealBase integer Base meal plan ID
mealSupplements string Meal supplements config
stopSale boolean Stop sale flag
available boolean Availability flag
onRequest boolean On request flag
mlos integer Minimum length of stay
mlosDesc string MLOS description
sdmlos integer Special day MLOS
notes string Entry notes

CMealPlan

Field Type Description
planId integer Plan identifier
plan string Plan code (RO, BB, HB, FB, AI)
name string Plan name
planDesc string Plan description
planOrder integer Display order
adultCost number Adult supplement cost
childCost number Child supplement cost

CHotelSearchResult

Field Type Description
input CHotelSearchInput Search parameters
results array Array of CHotelSearchResultItem

CHotelSearchResultItem

Field Type Description
hotel CHotel Hotel details
resultSource string Result source (DIRECT, GDS)
rooms array Array of CRoomSearchResult

CHotelOffer (Online Search/Booking)

Used by online hotel search (/hotel/searchOffers with searchMode="online") and booking journey endpoints.

Field Type Description
offerId string Unique offer identifier (GoGlobal HotelSearchCode)
type string Room type description
available boolean Availability status
hotelId string Hotel identifier (GoGlobal hotel code)
hotelName string Hotel name
chainCode string Hotel chain code (Amadeus only)
cityCode string City code / GoGlobal city ID
hotelLatitude number Hotel latitude
hotelLongitude number Hotel longitude
roomType string Room basis / meal plan code (e.g., BB, HB)
roomCategory string Star rating category code
beds integer Number of rooms
bedType string Bed type description
roomDescription string Full room type description
currencyCode string 3-letter currency code
totalAmount number Total stay price
baseAmount number Base price before taxes
cancellationType string "NON_REFUNDABLE" or "REFUNDABLE"
cancellationDeadline string Free cancellation deadline (ISO 8601)
cancellationDescription string Human-readable cancellation terms
boardType string Board basis description
checkInDate string Check-in date
checkOutDate string Check-out date
commission string Commission information
category string Category information
description string Offer description

CHotelBookingConfirmation (Booking Response)

Returned by /hotel/createBooking for GoGlobal bookings.

Field Type Description
goBookingCode string GoGlobal permanent booking code
goReference string GoGlobal supplier reference
bookingStatus string Status: C (confirmed), RQ (on-request), RJ (rejected)
totalPrice number Confirmed booking price
currency string Currency code
hotelId string Hotel identifier
hotelName string Hotel name
hotelConfirmation string Hotel's own confirmation number
clientBookingCode string Agency reference code

CHotelPriceBreakdown (Price Breakdown Response)

Returned by /hotel/getPriceBreakdown.

Field Type Description
hotelSearchCode string The HotelSearchCode identifier
rooms array Array of room breakdown objects

Room Breakdown Object: | Field | Type | Description | |-------|------|-------------| | roomId | integer | Room sequence number | | nights | array | Array of nightly rate objects |

Nightly Rate Object: | Field | Type | Description | |-------|------|-------------| | date | string | Night date (yyyy-MM-dd) | | price | number | Price for that night | | currency | string | Currency code |


Hotel Package Endpoints

POST /hotel/listPackages

List packages, optionally filtering by hotel and/or status. Photos are excluded from list response for performance. Includes hotel name for cross-hotel display.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | hotelId | integer | No | Filter by hotel ID (omit for all hotels) | | status | string | No | Filter by status: DRAFT, ACTIVE, RETIRED |

Response: Array of CHotelPackage objects (without photo fields), each including a rooms array and hotelName.


POST /hotel/getPackage

Get full package details including rooms and photos.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | Yes | Package ID |

Response: CHotelPackage object with all fields including photos and rooms.

CHotelPackage Object: | Field | Type | Description | |-------|------|-------------| | packageId | integer | Package ID (auto-generated) | | hotelId | integer | Hotel ID | | packageName | string | Package display name | | area | string | Hotel/destination area | | stayFrom | date | Stay validity start | | stayTo | date | Stay validity end | | bookFrom | date | Booking window start | | bookTo | date | Booking window end | | nights | integer | Number of nights | | mealPlan | string | Meal plan code (RO, BB, HB, FB, AIL, AI, AIP) | | maxAdults | integer | Maximum adults | | freeChildren | integer | Number of free children | | freeChildAge | integer | Max age for free children | | discChildren | integer | Number of discounted children | | discChildAge | integer | Max age for discounted children | | description | string | Rich text (HTML) description | | websitePhoto | string | Base64 encoded photo for website | | adPhoto | string | Base64 encoded photo for ad | | brochurePhoto | string | Base64 encoded photo for brochure/PDF | | blackoutDates | string | Comma-separated ISO dates | | status | string | Package status: DRAFT, ACTIVE, RETIRED | | hotelName | string | Hotel name (populated in list responses) | | rooms | array | Array of CHotelPackageRoom objects |

CHotelPackageRoom Object: | Field | Type | Description | |-------|------|-------------| | packageRoomId | integer | Room entry ID | | packageId | integer | Parent package ID | | roomType | string | Room type name (free text) | | pricePerNight | number | Price per night | | priceTotal | number | Total package price | | currencyCode | string | Currency code (AED, USD, EUR) | | sortOrder | integer | Display order |


POST /hotel/savePackage

Save a package with rooms atomically. Rooms are replaced entirely on each save.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | No | 0 or null for new package | | hotelId | integer | Yes | Hotel ID | | packageName | string | Yes | Package name | | rooms | array | No | Array of room objects | | (all CHotelPackage fields) | | | See getPackage response |

Response: Saved CHotelPackage object with generated IDs.


POST /hotel/deletePackage

Delete a package and all its rooms. Requires admin role.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | Yes | Package ID to delete |

Response: "OK" on success.


POST /hotel/generatePackagePdf

Generate a one-page PDF brochure for a package. Company info is loaded from server config (tqpro.company.* properties).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | Yes | Package ID |

Response: | Field | Type | Description | |-------|------|-------------| | pdf | string | Base64-encoded PDF content | | filename | string | Suggested filename |


POST /hotel/copyPackage

Copy a package as a new DRAFT (for reactivating retired packages). Copies all fields including rooms, photos, and description. The new package gets status DRAFT.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | Yes | Package ID to copy |

Response: New CHotelPackage object with generated ID and status DRAFT.


POST /hotel/retirePackages

Manually trigger retirement of all ACTIVE packages whose stay-to date is in the past. Admin only. (Also runs automatically daily via scheduled job.)

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token |

Response: { "retired": <count> }


POST /hotel/packageTemplate

Download an Excel template for bulk package import. The workbook contains two sheets: "Packages" (import sheet with data validation dropdowns) and "Hotels" (reference sheet with all hotels from DB for VLOOKUP).

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token |

Response: Binary .xlsx file (application/octet-stream).


POST /hotel/importPackages

Import packages from an uploaded Excel file. Rows are grouped by (Hotel Name + Package Name). Upserts packages by matching hotelId + packageName. Rooms are merged by room type — existing rooms not in the Excel are preserved.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | fileData | string | Yes | Base64-encoded Excel file content |

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "totalProcessed": 9,
    "inserted": 2,
    "updated": 1,
    "failed": 0,
    "errors": []
  }
}

Error objects in errors array: | Field | Type | Description | |-------|------|-------------| | rowNumber | integer | Excel row number (1-based) | | fieldName | string | Field that caused the error | | errorMessage | string | Human-readable error description | | rawValue | string | Original cell value |

Limits: 5MB file size, 500 rows max.


POST /hotel/exportHotels

Download all configured hotels as an Excel file. Each row represents one hotel with all basic-section fields.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token |

Response: Binary .xlsx file (application/octet-stream, filename hotels.xlsx).

Columns: Hotel Code*, Hotel Name*, Short Name, Country, City, Area, Stars, Direct Contract, Indirect/DMC, Infant Age (up to), Child Age (up to), Check-in Time, Check-out Time, Release Days, Service Fees, Fee Description, Terms & Conditions, Description, Reservation Contact

Data Validations: Area (DXB, AUH, AIN, SHJ, AJM, FUJ, RAK, UAQ), Stars (1–5), Direct/Indirect (Yes/No).


POST /hotel/importHotels

Import hotels from an uploaded Excel file. Performs upsert matching by hotel code (shDes, case-insensitive). Existing hotels are updated; new codes are inserted.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | fileData | string | Yes | Base64-encoded Excel file content |

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "totalProcessed": 15,
    "inserted": 5,
    "updated": 10,
    "failed": 0,
    "errors": []
  }
}

Error objects in errors array: | Field | Type | Description | |-------|------|-------------| | rowNumber | integer | Excel row number (1-based) | | fieldName | string | Field that caused the error | | errorMessage | string | Human-readable error description | | rawValue | string | Original cell value |

Required fields: Hotel Code, Hotel Name. Rows missing either are skipped with an error.

Preserve behavior: When updating an existing hotel, empty Description, Terms, and Reservation Contact cells preserve the existing database values.

Limits: 5MB file size, 500 rows max.


Package Booking Endpoints

POST /hotel/booking/create

Create a draft booking for a stay package.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | Yes | Package ID | | hotelId | integer | No | Hotel ID | | hotelName | string | No | Hotel name | | checkIn | string | No | Check-in date (yyyy-MM-dd) | | checkOut | string | No | Check-out date (yyyy-MM-dd) | | nights | integer | No | Number of nights | | mealPlan | string | No | Meal plan code | | customerName | string | Yes | Customer full name | | customerEmail | string | Yes | Customer email | | customerPhone | string | No | Customer phone | | odooCustomerId | integer | No | Pre-resolved Odoo customer ID | | passengers | string/array | No | JSON array of passenger objects | | rooms | string/array | No | JSON array of room selection objects | | salePrice | number | No | Total sale price (editable by agent) | | saleCurrency | string | No | Currency code | | notes | string | No | Booking notes |

Passenger object: { "firstName", "lastName", "phone", "email" } Room selection object: { "roomType", "quantity", "priceTotal", "currencyCode" }


POST /hotel/booking/read

Get a single booking by ID.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |


POST /hotel/booking/list

List bookings with optional filters.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | packageId | integer | No | Filter by package | | status | string | No | Filter by status: DRAFT, PENDING_PAYMENT, COMPLETED, CANCELLED |


POST /hotel/booking/update

Update booking details. Once status is PENDING_PAYMENT or COMPLETED, only reservationName, confirmationNumber, and notes can be updated.

Request Body: Same as create, with bookingId required.


POST /hotel/booking/checkout

Create Odoo quote, generate payment link. Changes booking status from DRAFT to PENDING_PAYMENT.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |

Response: Updated CPackageBooking with cartReference, telrPaymentLink, odooQuoteId.


POST /hotel/booking/confirmPayment

Manually confirm payment. Creates Odoo invoice, validates, and registers payment. Changes status to COMPLETED.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |


POST /hotel/booking/voucher

Generate PDF booking voucher with company header, reservation details, rooms, passengers, and confirmation number.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |

Response: { "pdf": "base64-encoded-pdf" }


POST /hotel/booking/invoice

Download invoice PDF from Odoo.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |

Response: { "pdf": "base64-encoded-pdf" }


POST /hotel/booking/sendInvoice

Email invoice PDF to the customer.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | bookingId | integer | Yes | Booking ID |

Response: "OK"


CPackageBooking Object

Field Type Description
bookingId integer Booking ID
packageId integer FK to package
hotelId integer FK to hotel
hotelName string Hotel name
checkIn date Check-in date
checkOut date Check-out date
nights integer Number of nights
mealPlan string Meal plan code
customerName string Customer name
customerEmail string Customer email
customerPhone string Customer phone
odooCustomerId integer Odoo customer ID
passengers string JSON array of passengers
rooms string JSON array of selected rooms
salePrice number Total sale price
saleCurrency string Currency code
cartReference string Cart/payment reference
odooQuoteId integer Odoo sale order ID
invoiceNumber string Odoo invoice number
telrPaymentLink string Telr payment URL
paymentStatus string Payment status
reservationName string Name on hotel reservation
confirmationNumber string Hotel confirmation number
status string DRAFT, PENDING_PAYMENT, COMPLETED, CANCELLED
agentId string Agent user ID
notes string Booking notes
created datetime Creation timestamp
updated datetime Last update timestamp

Supplier Hotel Import

POST /hotel/importFromSupplier

Import a hotel from an online supplier (GoGlobal) into the contracted hotels database. Creates or updates a CHotel and its CHotelRoom entries. Duplicate detection uses the supplierHotelId field.

Roles: agent, admin

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | Yes | User session token | | supplierHotelId | string | Yes | Supplier's hotel ID (e.g. "648898") | | hotelName | string | Yes | Hotel name | | stars | integer | No | Star rating (1-5) | | country | string | No | Country code (e.g. "DE") | | city | string | No | City name | | address | string | No | Hotel address | | description | string | No | Hotel description | | rooms | array | No | Array of room objects |

Room object: | Field | Type | Description | |-------|------|-------------| | name | string | Room type name (e.g. "Deluxe Double") | | bedding | string | Bed type (e.g. "Queen") | | maxOccupancy | integer | Maximum occupancy | | maxAdults | integer | Maximum adults |

Response Data: | Field | Type | Description | |-------|------|-------------| | hotel | object | The created/updated CHotel entity | | hotelId | integer | Hotel ID in the local database | | roomsSaved | integer | Number of rooms created/updated | | created | boolean | True if a new hotel was created | | updated | boolean | True if an existing hotel was updated |

Example Request:

{
  "session": "...",
  "supplierHotelId": "648898",
  "hotelName": "HOTEL-RESTAURANT SCHMACHTENBERGSHOF",
  "stars": 3,
  "country": "DE",
  "city": "ESSEN",
  "address": "Schmachtenbergstrasse 157, Essen, DE",
  "rooms": [
    { "name": "Deluxe Double", "bedding": "Queen", "maxOccupancy": 2, "maxAdults": 2 },
    { "name": "Standard Single", "bedding": "Single", "maxOccupancy": 1, "maxAdults": 1 }
  ]
}

Duplicate Detection: If a hotel with the same supplierHotelId already exists, the hotel and its rooms are updated instead of creating a duplicate. The shDes field is set to "GG" + supplierHotelId for new imports.


CDN Media Browser Integration

The hotel package edit page (hotel-package-edit.html) integrates the CDN Media Browser for the three photo URL fields: Website Image, Ad Image, and Brochure Image. Each field has a browse button that opens a modal for browsing existing S3 images and uploading new ones.

How It Works

Each photo URL input has a browse button with data-cdn-* attributes:

<div class="input-group">
  <input type="url" id="url_website" class="form-control form-control-sm photo-url-input" ...>
  <button type="button" class="btn btn-outline-secondary btn-sm"
          data-cdn-purpose="hotel-photos" data-cdn-allow-upload="true"
          data-cdn-target="#url_website">
    <i class="bi bi-folder2-open"></i>
  </button>
</div>

The cdn-browser.js module auto-binds to these buttons on page load. When clicked, the CDN browser modal opens with:

  • Browse tab: Navigate S3 folders under hotels/photos/, view image thumbnails via CloudFront, select an image
  • Upload tab: Select a template (Card Header 600x400, Page Banner 1920x400, etc.), drop an image, upload with automatic resize/crop/WebP conversion

On selection or upload, the CDN URL is written to the target input field and a change event is dispatched, triggering the existing image preview update.

Media API Endpoints Used

The CDN browser calls the Media API endpoints: - POST /media/cdn/browse — List folders and images - POST /media/cdn/upload — Upload with resize/crop/WebP - POST /media/cdn/templates — Get available image templates

Purpose Configuration

The hotel-photos purpose is configured in config/nts-client.xml:

<Purpose id="hotel-photos"
         bucket="tq-media"
         prefix="hotels/photos/"
         cdnBaseUrl="https://cdn.tripqlub.com"/>