Skip to content

Group API Specification

Overview

The Group API provides endpoints for managing group travel including groups, passengers (pax), hotels, transport, and room assignments.

Base Path: /groups

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)


Group Endpoints

POST /groups/group/write

Creates or updates a trip group.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | No | Group ID (for update, omit for create) | | groupName | string | Yes | Group name | | arrivalDate | string | Yes | Arrival date (yyyy-MM-dd) | | departureDate | string | Yes | Departure date (yyyy-MM-dd) | | partnerId | integer | No | Partner ID | | paxCount | integer | No | Expected passenger count | | notes | string | No | Group notes |

Request Example (Create):

{
  "session": "user-session-token",
  "groupName": "Summer Tour Group 2025",
  "arrivalDate": "2025-07-15",
  "departureDate": "2025-07-22",
  "partnerId": 500,
  "paxCount": 25,
  "notes": "VIP group from ABC Travel Agency"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "groupId": 1001,
    "groupName": "Summer Tour Group 2025",
    "arrivalDate": "2025-07-15T00:00:00",
    "departureDate": "2025-07-22T00:00:00",
    "partnerId": 500,
    "partnerName": "ABC Travel Agency",
    "paxCount": 25,
    "status": "CONFIRMED",
    "notes": "VIP group from ABC Travel Agency",
    "createdDate": "2025-06-15T10:30:00"
  }
}


POST /groups/group/list

Lists trip groups by date range.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | arrivalFrom | string | Yes | Start date (yyyy-MM-dd) | | arrivalTo | string | Yes | End date (yyyy-MM-dd) | | partnerId | integer | No | Filter by partner ID |

Request Example:

{
  "session": "user-session-token",
  "arrivalFrom": "2025-07-01",
  "arrivalTo": "2025-07-31",
  "partnerId": 500
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "groupId": 1001,
      "groupName": "Summer Tour Group 2025",
      "arrivalDate": "2025-07-15T00:00:00",
      "departureDate": "2025-07-22T00:00:00",
      "partnerId": 500,
      "partnerName": "ABC Travel Agency",
      "paxCount": 25,
      "status": "CONFIRMED"
    },
    {
      "groupId": 1002,
      "groupName": "Corporate Retreat",
      "arrivalDate": "2025-07-20T00:00:00",
      "departureDate": "2025-07-25T00:00:00",
      "partnerId": 500,
      "partnerName": "ABC Travel Agency",
      "paxCount": 15,
      "status": "PENDING"
    }
  ]
}

Error Codes: - INVALID_FORMAT - Date format incorrect


POST /groups/group/loadExpanded

Loads a group with all related data.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID to load |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "group": {
      "groupId": 1001,
      "groupName": "Summer Tour Group 2025",
      "arrivalDate": "2025-07-15T00:00:00",
      "departureDate": "2025-07-22T00:00:00",
      "partnerId": 500,
      "partnerName": "ABC Travel Agency",
      "paxCount": 25,
      "status": "CONFIRMED"
    },
    "passengers": [
      {
        "paxId": 2001,
        "firstName": "John",
        "lastName": "Smith",
        "dateOfBirth": "1985-03-15",
        "passportNumber": "AB1234567"
      },
      {
        "paxId": 2002,
        "firstName": "Jane",
        "lastName": "Smith",
        "dateOfBirth": "1987-06-20",
        "passportNumber": "AB1234568"
      }
    ],
    "hotels": [
      {
        "hotelId": 3001,
        "hotelName": "Hilton Dubai Downtown",
        "checkIn": "2025-07-15T14:00:00",
        "checkOut": "2025-07-22T12:00:00",
        "roomCount": 13
      }
    ],
    "transport": [
      {
        "transportId": 4001,
        "type": "FLIGHT",
        "departure": "2025-07-15T08:00:00",
        "arrival": "2025-07-15T12:00:00",
        "flightNumber": "EK5",
        "from": "LHR",
        "to": "DXB"
      }
    ],
    "rooms": [
      {
        "roomId": 5001,
        "hotelId": 3001,
        "roomType": "TWIN",
        "roomNumber": "501"
      }
    ],
    "roomAssignments": [
      {
        "roomPaxId": 6001,
        "roomId": 5001,
        "paxId": 2001,
        "isPrimary": true
      },
      {
        "roomPaxId": 6002,
        "roomId": 5001,
        "paxId": 2002,
        "isPrimary": false
      }
    ]
  }
}


Passenger Endpoints

POST /groups/pax/write

Creates or updates passengers.

Request Body (single pax): | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | paxId | integer | No | Pax ID (for update) | | groupId | integer | Yes | Group ID | | firstName | string | Yes | First name | | lastName | string | Yes | Last name | | dateOfBirth | string | No | Date of birth (yyyy-MM-dd) | | passportNumber | string | No | Passport number | | nationality | string | No | Nationality | | gender | string | No | Gender (M/F) |

Request Example (single):

{
  "session": "user-session-token",
  "groupId": 1001,
  "firstName": "Michael",
  "lastName": "Johnson",
  "dateOfBirth": "1990-08-25",
  "passportNumber": "CD9876543",
  "nationality": "US",
  "gender": "M"
}

Request Example (multiple):

{
  "session": "user-session-token",
  "paxList": [
    {
      "groupId": 1001,
      "firstName": "Michael",
      "lastName": "Johnson",
      "dateOfBirth": "1990-08-25"
    },
    {
      "groupId": 1001,
      "firstName": "Sarah",
      "lastName": "Johnson",
      "dateOfBirth": "1992-04-12"
    }
  ]
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "paxId": 2003,
    "groupId": 1001,
    "firstName": "Michael",
    "lastName": "Johnson",
    "dateOfBirth": "1990-08-25T00:00:00",
    "passportNumber": "CD9876543",
    "nationality": "US",
    "gender": "M"
  }
}


POST /groups/pax/read

Retrieves a single passenger by ID.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | paxId | integer | Yes | Passenger ID |

Request Example:

{
  "session": "user-session-token",
  "paxId": 2001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "paxId": 2001,
    "groupId": 1001,
    "firstName": "John",
    "lastName": "Smith",
    "dateOfBirth": "1985-03-15T00:00:00",
    "passportNumber": "AB1234567",
    "nationality": "GB",
    "gender": "M",
    "email": "john.smith@example.com",
    "phone": "+447123456789"
  }
}


POST /groups/pax/list

Lists passengers for a group.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | No | Group ID to filter by |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "paxId": 2001,
      "groupId": 1001,
      "firstName": "John",
      "lastName": "Smith",
      "dateOfBirth": "1985-03-15T00:00:00",
      "passportNumber": "AB1234567"
    },
    {
      "paxId": 2002,
      "groupId": 1001,
      "firstName": "Jane",
      "lastName": "Smith",
      "dateOfBirth": "1987-06-20T00:00:00",
      "passportNumber": "AB1234568"
    }
  ]
}


POST /groups/pax/template

Downloads an Excel template file for passenger import.

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

Request Example:

{
  "session": "user-session-token"
}

Response: Returns an Excel file (.xlsx) as binary stream with Content-Disposition header.

The template contains the following columns:

Column Header Required Format
A First Name* Yes Text
B Last Name* Yes Text
C Gender (M/F) No M or F
D Date of Birth No yyyy-MM-dd
E Nationality No Text
F Passport Number No Text
G Phone No Text
H Email No Text
I Age No Integer
J Notes No Text

POST /groups/pax/import

Imports passengers from an uploaded Excel file. Performs duplicate detection based on last name and passport number: matching passengers are updated, non-matching ones are inserted.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Target group ID | | fileData | string | Yes | Base64-encoded Excel file content | | fileName | string | No | Original file name |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "fileData": "UEsDBBQAAAAIAA1RVVYAAAA...",
  "fileName": "group_passengers.xlsx"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "totalProcessed": 10,
    "inserted": 7,
    "updated": 2,
    "failed": 1,
    "errors": [
      {
        "rowNumber": 5,
        "fieldName": "paxFirstName",
        "errorMessage": "First name is required",
        "rawValue": ""
      }
    ],
    "importedPax": [
      {
        "paxId": 2010,
        "groupId": 1001,
        "paxFirstName": "Michael",
        "paxLastName": "Johnson",
        "gender": "M",
        "birthDate": "1990-08-25T00:00:00",
        "nationality": "US",
        "passportNum": "CD9876543",
        "contactTel": "+1-555-1234",
        "contactEmail": "michael@email.com",
        "age": 34,
        "note": "Window seat"
      }
    ]
  }
}

Error Codes: | Code | Description | |------|-------------| | MISSING_PARAMETER | groupId or fileData not provided | | INVALID_FORMAT | File is not valid Excel or exceeds 5MB limit | | GENERAL | Unexpected error during import |


Export Endpoints

POST /groups/pax/exportExcel

Exports all passengers for a group as an Excel (.xlsx) file.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID to export passengers for |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response: Returns an Excel file (.xlsx) as binary stream (application/octet-stream) with Content-Disposition: attachment; filename="passengers_group_1001.xlsx" header.

On error, returns a JSON TlinqApiResponse with application/json content type.

Columns: First Name, Last Name, Gender, Date of Birth, Nationality, Passport Number, Phone, Email, Age, Notes, Emergency Contact, Emergency Tel, Visa Required, Visa Status


POST /groups/exportSchedule

Exports a schedule of upcoming events across multiple groups as an Excel (.xlsx) file.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupIds | integer[] | Yes | Array of group IDs to include | | rangeDays | integer | No | Number of days from today (default: 30) |

Request Example:

{
  "session": "user-session-token",
  "groupIds": [1001, 1002, 1003],
  "rangeDays": 30
}

Response: Returns an Excel file (.xlsx) as binary stream (application/octet-stream) with Content-Disposition: attachment; filename="group_schedule.xlsx" header.

On error, returns a JSON TlinqApiResponse with application/json content type.

Excel structure: - Title row: "Incoming Groups Schedule" - Subtitle: date range - Columns: Group, Event Type, Event Description, Date / Time, Status - Rows grouped by date with grey separator rows - Event types: Check-in, Check-out (hotels), Transport, Activity - Status column: readiness signals (OK, No confirmation, No driver, Not booked, Tickets needed, etc.) - Conditional cell formatting: yellow for warnings, green for OK

Error Codes: | Code | Description | |------|-------------| | MISSING_PARAMETER | groupIds not provided | | NOTFOUND | No groups found for the given IDs | | GENERAL | Unexpected error during export |

Note: Both export endpoints return binary data. The standard tlinq() function (which expects JSON) cannot handle these responses. The frontend uses fetch() with Bearer auth to download the binary blob directly.


Hotel Endpoints

POST /groups/hotel/list

Lists hotels for a group.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "hotelId": 3001,
      "groupId": 1001,
      "hotelName": "Hilton Dubai Downtown",
      "hotelAddress": "Downtown Dubai",
      "checkIn": "2025-07-15T14:00:00",
      "checkOut": "2025-07-22T12:00:00",
      "roomCount": 13,
      "confirmationNumber": "HLT-123456"
    }
  ]
}


POST /groups/hotel/write

Creates or updates a hotel assignment.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | hotelId | integer | No | Hotel ID (for update) | | groupId | integer | Yes | Group ID | | hotelName | string | Yes | Hotel name | | checkIn | string | Yes | Check-in date | | checkOut | string | Yes | Check-out date | | roomCount | integer | No | Number of rooms |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "hotelName": "Marriott Al Jaddaf",
  "hotelAddress": "Al Jaddaf, Dubai",
  "checkIn": "2025-07-18",
  "checkOut": "2025-07-20",
  "roomCount": 13
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "hotelId": 3002,
    "groupId": 1001,
    "hotelName": "Marriott Al Jaddaf",
    "hotelAddress": "Al Jaddaf, Dubai",
    "checkIn": "2025-07-18T14:00:00",
    "checkOut": "2025-07-20T12:00:00",
    "roomCount": 13
  }
}


Transport Endpoints

POST /groups/transport/list

Lists transport for a group.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "transportId": 4001,
      "groupId": 1001,
      "type": "FLIGHT",
      "departure": "2025-07-15T08:00:00",
      "arrival": "2025-07-15T12:00:00",
      "flightNumber": "EK5",
      "from": "LHR",
      "to": "DXB",
      "carrier": "Emirates"
    },
    {
      "transportId": 4002,
      "groupId": 1001,
      "type": "COACH",
      "departure": "2025-07-15T13:00:00",
      "arrival": "2025-07-15T14:00:00",
      "from": "DXB Airport",
      "to": "Hilton Dubai Downtown",
      "vehicleType": "Luxury Coach 50-seater"
    }
  ]
}


POST /groups/transport/write

Creates or updates a transport entry.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | transportId | integer | No | Transport ID (for update) | | groupId | integer | Yes | Group ID | | type | string | Yes | Transport type (FLIGHT, COACH, FERRY) | | departure | string | Yes | Departure datetime | | arrival | string | Yes | Arrival datetime | | from | string | Yes | Origin | | to | string | Yes | Destination |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "type": "FLIGHT",
  "departure": "2025-07-22T14:00:00",
  "arrival": "2025-07-22T18:15:00",
  "flightNumber": "EK6",
  "from": "DXB",
  "to": "LHR",
  "carrier": "Emirates"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "transportId": 4003,
    "groupId": 1001,
    "type": "FLIGHT",
    "departure": "2025-07-22T14:00:00",
    "arrival": "2025-07-22T18:15:00",
    "flightNumber": "EK6",
    "from": "DXB",
    "to": "LHR",
    "carrier": "Emirates"
  }
}


Room Endpoints

POST /groups/triproom/list

Lists rooms for a group.

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

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "hotelId": 3001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "roomId": 5001,
      "groupId": 1001,
      "hotelId": 3001,
      "roomType": "TWIN",
      "roomNumber": "501",
      "floor": "5",
      "notes": null
    },
    {
      "roomId": 5002,
      "groupId": 1001,
      "hotelId": 3001,
      "roomType": "DOUBLE",
      "roomNumber": "502",
      "floor": "5",
      "notes": "Accessible room"
    }
  ]
}


POST /groups/triproom/write

Creates or updates a room.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | roomId | integer | No | Room ID (for update) | | groupId | integer | Yes | Group ID | | hotelId | integer | Yes | Hotel ID | | roomType | string | Yes | Room type | | roomNumber | string | No | Room number |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "hotelId": 3001,
  "roomType": "SUITE",
  "roomNumber": "601"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "roomId": 5003,
    "groupId": 1001,
    "hotelId": 3001,
    "roomType": "SUITE",
    "roomNumber": "601"
  }
}


POST /groups/triproom/delete

Deletes a room.

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

Request Example:

{
  "session": "user-session-token",
  "room": {
    "roomId": 5003
  }
}

Response Structure:

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


POST /groups/triproom/listpax

Lists passengers assigned to rooms.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID | | roomId | integer | No | Room ID filter |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "roomPaxId": 6001,
      "roomId": 5001,
      "paxId": 2001,
      "paxName": "John Smith",
      "isPrimary": true
    },
    {
      "roomPaxId": 6002,
      "roomId": 5001,
      "paxId": 2002,
      "paxName": "Jane Smith",
      "isPrimary": false
    }
  ]
}


POST /groups/triproom/writepax

Assigns passengers to a room.

Request Body (single): | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | roomId | integer | Yes | Room ID | | paxId | integer | Yes | Passenger ID | | isPrimary | boolean | No | Primary guest flag |

Request Example:

{
  "session": "user-session-token",
  "roomId": 5002,
  "paxId": 2003,
  "isPrimary": true
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "roomPaxId": 6003,
    "roomId": 5002,
    "paxId": 2003,
    "isPrimary": true
  }
}


POST /groups/triproom/removepax

Removes a passenger from a room.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | roomId | integer | Yes | Room ID | | paxId | integer | Yes | Passenger ID |

Request Example:

{
  "session": "user-session-token",
  "roomId": 5002,
  "paxId": 2003
}

Response Structure:

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


Visa Endpoints

POST /groups/pax/checkVisa

Performs bulk visa requirement check for all passengers in a group based on their nationality and the trip's destination country.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID | | destination | string | Yes | Destination country code (ISO Alpha-2 or Alpha-3) |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "destination": "TUR"
}

Response Structure: Returns the updated passenger list with visa fields populated.

Visa Required Values: | Value | Description | |-------|-------------| | NOT_REQUIRED | Visa not required (visa free / freedom of movement) | | VOA | Visa on arrival | | ONLINE | Online visa / e-visa / eTA (Electronic Travel Authorization) | | REQUIRED | Visa required (must apply in advance) | | UNKNOWN | Could not determine (fallback) |

Classification logic: Uses a data-driven rules array matching visa_rules.primary_rule.name from the TravelBuddyAI v2 API response. See Visa Category Classification for the full keyword list and matching rules.

Same-country optimization: If a passenger's nationality matches the destination country, the system returns NOT_REQUIRED immediately without calling the external API.

Visa Status Values: | Value | Description | |-------|-------------| | DOC_PENDING | Documents not yet collected | | DOC_COMPLETE | Documents collected, ready to apply | | APPLIED | Visa application submitted | | APPROVED | Visa approved | | REJECTED | Visa rejected | | NOT_NEEDED | Visa not needed for this destination |


POST /groups/pax/createVisaApp

Creates a visa application for a passenger. This performs three steps: 1. Creates a customer record in the CRM (Odoo) using the passenger's name, phone, and email 2. Creates a visa application and applicant record in the visa module 3. Links the passenger to the visa application via visaApplicationId and sets visa status to DOC_PENDING

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | paxId | integer | Yes | Passenger ID |

Request Example:

{
  "session": "user-session-token",
  "paxId": 2001
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "visaApplicationId": 150,
    "paxId": 2001
  }
}

Error Conditions: - Passenger not found - Passenger already has a visa application (visaApplicationId is set) - Customer creation failed - Missing required passenger fields (phone, email)


Extra Cost Endpoints

POST /groups/extracost/list

Lists extra costs for a trip service.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | tripServiceId | integer | Yes | Trip service/activity ID |

Request Example:

{
  "session": "user-session-token",
  "tripServiceId": 789
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "serviceExtraCostId": 1,
      "tripServiceId": 789,
      "description": "Guide fee",
      "cost": 150.00,
      "currencyCode": "AED"
    }
  ]
}


POST /groups/extracost/write

Creates or updates an extra cost for an activity.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | serviceExtraCostId | integer | No | Extra cost ID (for update) | | tripServiceId | integer | Yes | Trip service/activity ID | | description | string | Yes | Description of the extra cost | | cost | double | Yes | Cost amount | | currencyCode | string | No | Currency code (3 chars) |

Request Example:

{
  "session": "user-session-token",
  "tripServiceId": 789,
  "description": "Parking fee",
  "cost": 50.00,
  "currencyCode": "AED"
}


POST /groups/extracost/delete

Deletes an extra cost.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | serviceExtraCostId | integer | Yes | Extra cost ID to delete |


Ticket Requirement Endpoints

POST /groups/ticketreq/list

Lists ticket requirements for an activity, enriched with fulfilled counts from offline ticket sales.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | tripServiceId | integer | Yes | Trip service/activity ID |

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": [
    {
      "serviceTicketReqId": 1,
      "tripServiceId": 789,
      "attractionId": 42,
      "attractionName": "Burj Khalifa - At the Top",
      "quantity": 25,
      "fulfilled": 20
    }
  ]
}


POST /groups/ticketreq/write

Creates or updates a ticket requirement for an activity.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | serviceTicketReqId | integer | No | Ticket requirement ID (for update) | | tripServiceId | integer | Yes | Trip service/activity ID | | attractionId | integer | Yes | FK to offline attraction | | quantity | integer | Yes | Number of tickets required |


POST /groups/ticketreq/delete

Deletes a ticket requirement.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | serviceTicketReqId | integer | Yes | Ticket requirement ID to delete |


PDF Quotation Endpoint

POST /groups/quotation/generatePdf

Generates a professional PDF quotation for a group, including company header, accommodation, activities, transport, cost breakdown, and a chronological schedule.

Request Body: | Field | Type | Required | Description | |-------|------|----------|-------------| | session | string | No | User session token | | groupId | integer | Yes | Group ID | | quoteItems | array | Yes | Array of quote line items | | quoteCurrency | string | Yes | Currency code for the quote |

Each quote item: | Field | Type | Description | |-------|------|-------------| | description | string | Item description | | quantity | number | Quantity | | price | number | Unit price | | lineTotal | number | Line total |

Request Example:

{
  "session": "user-session-token",
  "groupId": 1001,
  "quoteItems": [
    { "description": "Hotel Accommodation (7 nights)", "quantity": 1, "price": 5000.00, "lineTotal": 5000.00 },
    { "description": "City Tour - Adult", "quantity": 20, "price": 120.00, "lineTotal": 2400.00 },
    { "description": "Airport Transfer", "quantity": 2, "price": 800.00, "lineTotal": 1600.00 }
  ],
  "quoteCurrency": "AED"
}

Response Structure:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "pdf": "base64-encoded-pdf-bytes"
  }
}

PDF Content: - Company header: logo and company info loaded from tourlinq.properties (tqpro.company.name, tqpro.company.contact.1, tqpro.company.contact.2, tqpro.company.logo.url) - Group information: name, partner, dates, pax count, nights - Accommodation table: hotels with check-in/check-out dates - Activities table: services with date/time, adult/child counts - Transport table: transports with description, date/time, vehicle, duration (hours) - Cost breakdown: itemized quote lines with totals - Chronological schedule: all activities and transports sorted by date/time

Error Codes: | Code | Description | |------|-------------| | MISSING_PARAMETER | groupId not provided | | NOTFOUND | Group not found | | GENERAL | Unexpected error during PDF generation |


Data Models

CTripGroup

Field Type Description
groupId integer Unique group identifier
groupName string Group name
arrivalDate datetime Arrival date
departureDate datetime Departure date
partnerId integer Partner ID
partnerName string Partner name
paxCount integer Expected passenger count
status string Group status (PENDING, CONFIRMED, CANCELLED)
notes string Group notes
createdDate datetime Creation date
destinationCountry string Destination country ISO Alpha-3 code (outbound groups)
destinationCity string Destination city name (outbound groups)

CTripPax

Field Type Description
paxId integer Passenger identifier
groupId integer Group ID
firstName string First name
lastName string Last name
dateOfBirth datetime Date of birth
passportNumber string Passport number
nationality string Nationality display name
nationalityCode string Nationality ISO Alpha-3 country code
gender string Gender (M/F)
email string Email address (mandatory for outbound)
phone string Phone number (mandatory for outbound)
visaRequired string Visa requirement: UNKNOWN, NOT_REQUIRED, VOA, ONLINE, REQUIRED
visaStatus string Visa status: DOC_PENDING, DOC_COMPLETE, APPLIED, APPROVED, REJECTED, NOT_NEEDED
visaApplicationId integer FK to visa application (links to visa module)

CTripHotel

Field Type Description
hotelId integer Hotel assignment ID
groupId integer Group ID
hotelName string Hotel name
hotelAddress string Hotel address
checkIn datetime Check-in date
checkOut datetime Check-out date
roomCount integer Number of rooms
confirmationNumber string Booking confirmation

CTripTransport

Field Type Description
transportId integer Transport ID
groupId integer Group ID
type string Transport type (FLIGHT, COACH, FERRY)
departure datetime Departure time
arrival datetime Arrival time
from string Origin location
to string Destination location
flightNumber string Flight number (if flight)
carrier string Carrier name
vehicleType string Vehicle type (if ground)
duration integer Duration in hours
activityId integer FK to trip service (set for auto-created transfers)
confirmed boolean Transport confirmation status

CTripRoom

Field Type Description
roomId integer Room ID
groupId integer Group ID
hotelId integer Hotel ID
roomType string Room type (SINGLE, TWIN, DOUBLE, SUITE)
roomNumber string Room number
floor string Floor
notes string Room notes

CTripRoomPax

Field Type Description
roomPaxId integer Assignment ID
roomId integer Room ID
paxId integer Passenger ID
paxName string Passenger name
isPrimary boolean Primary guest flag

CServiceExtraCost

Field Type Description
serviceExtraCostId integer Extra cost ID
tripServiceId integer FK to trip service/activity
description string Description of the extra cost
cost double Cost amount
currencyCode string Currency code (3 chars)

CServiceTicketReq

Field Type Description
serviceTicketReqId integer Ticket requirement ID
tripServiceId integer FK to trip service/activity
attractionId integer FK to offline attraction
attractionName string Attraction display name (via lookup)
quantity integer Number of tickets required
fulfilled integer Tickets already fulfilled (transient, from offline ticket sales)

PaxImportResult

Field Type Description
totalProcessed integer Total rows processed from Excel file
inserted integer Number of new passengers created
updated integer Number of existing passengers updated
failed integer Number of rows that failed to import
errors array List of PaxRowError objects
importedPax array List of successfully imported CTripPax objects

PaxRowError

Field Type Description
rowNumber integer Excel row number (1-based, excluding header)
fieldName string Field that caused the error
errorMessage string Human-readable error description
rawValue string Original value from the Excel cell

Booking Integration

POST /groups/group/createBooking

Creates or updates a BLM booking from an inbound group. If an existing booking in ENQUIRY or QUOTED status is found for this group, it is updated in-place with the current group data.

Request Body:

Parameter Type Required Description
session String No Authentication session (extracted from security context in OIDC mode)
groupId Integer Yes The inbound group ID

Response: CBooking object with all fields populated.

Example Request:

{
  "session": "",
  "groupId": 7
}

Example Response:

{
  "apiStatus": { "errorCode": "OK", "errorMessage": "Success" },
  "apiData": {
    "bookingId": 102,
    "bookingRef": "TQ-2026-00043",
    "status": "ENQUIRY",
    "customerName": "Test Travel Agency",
    "sourceType": "GROUP_INBOUND",
    "sourceRef": "7",
    "travelStartDate": "2026-04-20",
    "travelEndDate": "2026-04-25"
  }
}

Behavior: - New booking: Creates booking with partner agency as customer, imports all hotels (one line per hotel), transports, services, and passengers - Existing ENQUIRY/QUOTED booking: Updates booking header, replaces all service lines and passengers with current group data - Existing CONFIRMED+ booking: Returns error

Error Responses: - Group not found → ERR00012 - Group is not INBOUND → ERR00014 - Existing booking past QUOTED → ERR00014 with message to cancel or amend manually

Roles: agent, admin