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:
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:
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:
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:
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:
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 | 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:
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:
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:
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:
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:
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:
Response Structure:
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:
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:
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:
Response Structure:
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:
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:
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:
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) |
| 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:
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