MENU navbar-image

Introduction

Authenticating requests

To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".

All authenticated endpoints are marked with a requires authentication badge in the documentation below.

Package Search

Get search options for travel form

requires authentication

Provides dynamic options for travel search form based on user selections. Returns specific data based on the requested mode to optimize performance.

This endpoint supports 4 different modes, each optimized for specific use cases:

Mode: departure_locations

Mode: destination_locations

Mode: available_dates

Mode: available_nights

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/search-options"
);

const params = {
    "lang": "ru",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "mode": "departure_locations",
    "departure_id": "\"202553\"",
    "destination_id": "\"123\"",
    "departure_date": "\"2024-06-15\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/packages/search-options?lang=ru" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"mode\": \"departure_locations\",
    \"departure_id\": \"\\\"202553\\\"\",
    \"destination_id\": \"\\\"123\\\"\",
    \"departure_date\": \"\\\"2024-06-15\\\"\"
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/search-options';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'lang' => 'ru',
        ],
        'json' => [
            'mode' => 'departure_locations',
            'departure_id' => '"202553"',
            'destination_id' => '"123"',
            'departure_date' => '"2024-06-15"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200, departure_locations):


{
    "data": [
        {
            "id": 202553,
            "airport_code": "BUH",
            "active": true,
            "destination": "Bucuresti",
            "country": "Romania"
        },
        {
            "id": 197276,
            "airport_code": "TSR",
            "destination": "Timisoara",
            "region": "Judetul Timis",
            "country": "Romania"
        },
        {
            "id": 200366,
            "airport_code": "IAS",
            "destination": "Iasi",
            "region": "Judetul Iasi",
            "country": "Romania"
        }
    ]
}
 

Example response (200, destination_locations):


{
    "data": [
        {
            "id": 532,
            "airport_code": "VAR",
            "type": "bus",
            "region": "Varna",
            "country": "Bulgaria"
        },
        {
            "id": 269,
            "airport_code": "DXB",
            "type": "flight",
            "region": "Dubai",
            "country": "Emiratele Arabe Unite"
        },
        {
            "id": 1364,
            "airport_code": null,
            "type": "flight",
            "region": "Attica",
            "country": "Grecia"
        }
    ]
}
 

Example response (200, available_dates):


{
    "data": [
        "2025-09-03",
        "2025-09-08",
        "2025-09-10",
        "2025-09-15",
        "2025-09-20"
    ]
}
 

Example response (200, available_nights):


{
    "data": [
        3,
        5,
        7,
        10,
        14
    ]
}
 

Example response (422, missing_mode):


{
    "error": "Validation failed",
    "messages": {
        "mode": [
            "The mode field is required."
        ]
    }
}
 

Example response (422, invalid_mode):


{
    "error": "Validation failed",
    "messages": {
        "mode": [
            "The selected mode is invalid."
        ]
    }
}
 

Example response (422, missing_departure_id_for_destinations):


{
    "error": "Validation failed",
    "messages": {
        "departure_id": [
            "The departure id field is required when mode is destination_locations."
        ]
    }
}
 

Example response (422, missing_destination_id_for_dates):


{
    "error": "Validation failed",
    "messages": {
        "destination_id": [
            "The destination id field is required when mode is available_dates."
        ]
    }
}
 

Example response (422, missing_departure_date_for_nights):


{
    "error": "Validation failed",
    "messages": {
        "departure_date": [
            "The departure date field is required when mode is available_nights."
        ]
    }
}
 

Example response (422, invalid_date_format):


{
    "error": "Validation failed",
    "messages": {
        "departure_date": [
            "The departure date does not match the format Y-m-d."
        ]
    }
}
 

Example response (500):


{
    "error": "Internal server error",
    "message": "Failed to retrieve search options"
}
 

Request      

POST api/packages/search-options

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

lang   string  optional  

Language code for localization. Example: ru

Body Parameters

mode   string   

Mode of operation. Must be one of: departure_locations, destination_locations, available_dates, available_nights. Example: departure_locations

departure_id   string  optional  

ID of selected departure city. Required for destination_locations, available_dates, and available_nights modes. Optional for departure_locations mode (used to mark active location). Example: "202553"

destination_id   string  optional  

ID of selected destination. Required for available_dates and available_nights modes. Example: "123"

departure_date   string  optional  

Departure date in Y-m-d format. Required for available_nights mode. Example: "2024-06-15"

requires authentication

Initiates an asynchronous search across multiple travel providers. Returns a search_id that can be used to poll for results. Supports multiple providers (ChristianTour, OBS) with different execution types.

POST /api/packages/search

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/search"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "departure_date": "2025-11-28",
    "nights": 7,
    "transport": [],
    "departure_id": 202553,
    "destination_id": 269,
    "hotel_ids": [],
    "occupancy": [
        "architecto"
    ],
    "currency": "EUR",
    "ignore_domains": [],
    "max_pages": 10,
    "providers": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/packages/search" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"departure_date\": \"2025-11-28\",
    \"nights\": 7,
    \"transport\": [],
    \"departure_id\": 202553,
    \"destination_id\": 269,
    \"hotel_ids\": [],
    \"occupancy\": [
        \"architecto\"
    ],
    \"currency\": \"EUR\",
    \"ignore_domains\": [],
    \"max_pages\": 10,
    \"providers\": []
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/search';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'departure_date' => '2025-11-28',
            'nights' => 7,
            'transport' => [],
            'departure_id' => 202553,
            'destination_id' => 269,
            'hotel_ids' => [],
            'occupancy' => [
                'architecto',
            ],
            'currency' => 'EUR',
            'ignore_domains' => [],
            'max_pages' => 10,
            'providers' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (202):


{
    "success": true,
    "search_id": "550e8400-e29b-41d4-a716-446655440000",
    "providers": [
        "christian_tour",
        "obs"
    ],
    "estimated_completion_time": "30 seconds",
    "status_url": "http://localhost/api/packages/search/550e8400-e29b-41d4-a716-446655440000/results",
    "cancel_url": "http://localhost/api/packages/search/550e8400-e29b-41d4-a716-446655440000"
}
 

Example response (400):


{
    "success": false,
    "message": "No providers available for async search"
}
 

Example response (422):


{
    "success": false,
    "message": "Validation failed",
    "errors": {
        "departure_date": [
            "The departure date must be a date after today."
        ],
        "occupancy": [
            "The occupancy field is required."
        ]
    }
}
 

Example response (500):


{
    "success": false,
    "message": "Failed to initiate search",
    "error": "Database connection failed"
}
 

Get search results by session ID grouped by hotels

requires authentication

Retrieves the current status and results of an asynchronous search session. Results are grouped by hotels (provider + hotel_external_id) and paginated. Only the cheapest offer per hotel is returned with site-specific pricing applied. Hotel images are limited to first 4 images for performance optimization. Results can be filtered by provider, price range, or sorted by different criteria.

Response includes:

GET /api/packages/search/{searchId}/results

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000"/results"
);

const params = {
    "page": "1",
    "per_page": "20",
    "sort_by": "id",
    "sort_direction": "asc",
    "provider": "christian_tour",
    "min_price": "100",
    "max_price": "2000",
    "hotel_name": "'ho' will find 'Massa Hotel'

Note: After optimization (OfferMinPriceTracker), the system saves only the cheapest offer per hotel.
      The 'total_offers' field shows ALL processed offers, while 'total_hotels' shows unique saved hotels.",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000"/results?page=1&per_page=20&sort_by=id&sort_direction=asc&provider=christian_tour&min_price=100&max_price=2000&hotel_name=%27ho%27+will+find+%27Massa+Hotel%27%0A%0ANote%3A+After+optimization+%28OfferMinPriceTracker%29%2C+the+system+saves+only+the+cheapest+offer+per+hotel.%0A++++++The+%27total_offers%27+field+shows+ALL+processed+offers%2C+while+%27total_hotels%27+shows+unique+saved+hotels." \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000"/results';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'page' => '1',
            'per_page' => '20',
            'sort_by' => 'id',
            'sort_direction' => 'asc',
            'provider' => 'christian_tour',
            'min_price' => '100',
            'max_price' => '2000',
            'hotel_name' => ''ho' will find 'Massa Hotel'

Note: After optimization (OfferMinPriceTracker), the system saves only the cheapest offer per hotel.
      The 'total_offers' field shows ALL processed offers, while 'total_hotels' shows unique saved hotels.',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "search_id": "019908a1-180e-7345-a654-855c90860cb1",
    "status": "completed",
    "results": [
        {
            "hotel_key": "christian_tour_9910",
            "provider": "christian_tour",
            "hotel_external_id": "9910",
            "hotel_name": "Blend Club Aqua Resort",
            "hotel_slug": "blend-club-aqua-resort-9910",
            "destination_name": "Hurghada",
            "offers_with_min_price_count": 3,
            "hotel": {
                "id": "9910",
                "name": "Blend Club Aqua Resort",
                "classification": 4,
                "latitude": "27.123456",
                "longitude": "33.654321",
                "address": {
                    "city": "Hurghada",
                    "street": "KM 21, Sahl Hashesh Road",
                    "country": "Egypt",
                    "email": "info@resort.com",
                    "phone": "+20 123 456 789",
                    "website": "https://resort.com"
                },
                "images": [
                    {
                        "url": "https://images.provider.com/hotel-9910/image1.jpg"
                    },
                    {
                        "url": "https://images.provider.com/hotel-9910/image2.jpg"
                    }
                ],
                "facilities": [
                    {
                        "id": 8,
                        "name": "Restaurant"
                    },
                    {
                        "id": 12,
                        "name": "Swimming Pool"
                    }
                ],
                "destination": {
                    "name": "Hurghada",
                    "latitude": "27.123456",
                    "longitude": "33.654321",
                    "hierarchy_path": "Egypt → Hurghada"
                }
            },
            "offers": [
                {
                    "id": 12345,
                    "offer_id": "7a86dd23-c4c8-499c-bff9-c2b05dd52767",
                    "room_type": "DELUXE GARDEN VIEW",
                    "meal_plan": "ALL INCLUSIVE",
                    "meal_plan_category_id": 5,
                    "check_in": "2025-09-27",
                    "check_out": "2025-10-04",
                    "nights": 7,
                    "travel_price": 3943.4,
                    "travel_discount_price": 2,
                    "currency": "RON",
                    "transfer": {
                        "included": true,
                        "type": "airport-hotel"
                    },
                    "occupancy": {
                        "adults": 2,
                        "children": 0,
                        "children_ages": [],
                        "total": 2
                    },
                    "bus": {
                        "departure": {
                            "city": "Bucharest",
                            "datetime": "2025-09-27T08:00:00"
                        },
                        "arrival": {
                            "city": "Hurghada",
                            "datetime": "2025-09-27T14:00:00"
                        }
                    },
                    "flight": {
                        "outbound": [
                            {
                                "flight_number": "4206",
                                "airline": {
                                    "code": "A2",
                                    "name": "Animawings"
                                },
                                "departure": {
                                    "code": "OTP",
                                    "name": "Henri Coanda International Airport",
                                    "datetime": "2025-09-27T17:45:00"
                                },
                                "arrival": {
                                    "code": "HRG",
                                    "name": "Hurghada International Airport",
                                    "datetime": "2025-09-27T20:55:00"
                                },
                                "duration": 190,
                                "baggage": {
                                    "checked": "20kg",
                                    "hand": "7kg"
                                }
                            }
                        ],
                        "inbound": [
                            {
                                "flight_number": "4207",
                                "airline": {
                                    "code": "A2",
                                    "name": "Animawings"
                                },
                                "departure": {
                                    "code": "HRG",
                                    "name": "Hurghada International Airport",
                                    "datetime": "2025-10-04T23:55:00"
                                },
                                "arrival": {
                                    "code": "OTP",
                                    "name": "Henri Coanda International Airport",
                                    "datetime": "2025-10-04T03:30:00"
                                },
                                "duration": 215
                            }
                        ]
                    },
                    "tags": [
                        "beach",
                        "family-friendly",
                        "all-inclusive",
                        "5-star"
                    ]
                }
            ]
        }
    ],
    "pagination": {
        "current_page": 1,
        "per_page": 20,
        "total": 42,
        "last_page": 3,
        "has_more_pages": true
    },
    "search_progress": {
        "completed_providers": 1,
        "total_providers": 1,
        "percentage": 100,
        "is_completed": true
    },
    "provider_status": [
        {
            "provider": "christian_tour",
            "status": "completed",
            "results_count": 156,
            "last_poll_at": null,
            "completed_at": "2025-10-03T07:13:44.000000Z",
            "error_message": null
        }
    ],
    "meta": {
        "total_hotels": 42,
        "total_offers": 90000,
        "min_travel_price": 1250.5,
        "max_travel_price": 8900,
        "currency": "RON",
        "expires_at": "2025-10-03T07:43:19.000000Z",
        "created_at": "2025-10-03T07:13:19.000000Z",
        "last_updated": "2025-10-03T07:13:44.000000Z"
    }
}
 

Example response (401):


{
    "success": false,
    "message": "Unauthorized. Valid Site token required."
}
 

Example response (404):


{
    "success": false,
    "message": "Search session not found"
}
 

Example response (410):


{
    "success": false,
    "message": "Search session has expired"
}
 

Example response (500):


{
    "success": false,
    "message": "Failed to retrieve search results",
    "error": "Database error"
}
 

Request      

GET api/packages/search/{searchId}/results

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

searchId   string   

The search session UUID. Example: "550e8400-e29b-41d4-a716-446655440000"

Query Parameters

page   integer  optional  

optional Page number for pagination. Example: 1

per_page   integer  optional  

optional Hotels per page. Max: 100. Default: 20. Example: 20

sort_by   string  optional  

optional Sort field. Values: id (offer database ID), min_price (cheapest offer), max_price (most expensive offer), found_at, destination_name, hotel_name, offers_count. Default: id. Example: id

sort_direction   string  optional  

optional Sort direction. Values: asc (ascending), desc (descending). Default: asc. Example: asc

provider   string  optional  

optional Filter by specific provider. Example: christian_tour

min_price   number  optional  

optional Minimum price filter (applied to travel_price with commissions). Example: 100

max_price   number  optional  

optional Maximum price filter (applied to travel_price with commissions). Example: 2000

hotel_name   string  optional  

optional Filter by hotel name. Searches for words that start with the input. Example: `'ho' will find 'Massa Hotel'

Note: After optimization (OfferMinPriceTracker), the system saves only the cheapest offer per hotel. The 'total_offers' field shows ALL processed offers, while 'total_hotels' shows unique saved hotels.`

Cancel search session

requires authentication

Cancels an active search session and stops all running provider jobs. Only sessions in 'pending' or 'in_progress' status can be cancelled.

DELETE /api/packages/search/{searchId}

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000""
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "DELETE",
    headers,
}).then(response => response.json());
curl --request DELETE \
    "https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000"" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/search/"550e8400-e29b-41d4-a716-446655440000"';
$response = $client->delete(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "message": "Search session cancelled",
    "search_id": "550e8400-e29b-41d4-a716-446655440000"
}
 

Example response (400):


{
    "success": false,
    "message": "Search session cannot be cancelled",
    "current_status": "completed"
}
 

Example response (404):


{
    "success": false,
    "message": "Search session not found"
}
 

Example response (500):


{
    "success": false,
    "message": "Failed to cancel search session",
    "error": "Database error"
}
 

Request      

DELETE api/packages/search/{searchId}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

searchId   string   

The search session UUID to cancel. Example: "550e8400-e29b-41d4-a716-446655440000"

Get all offers for a specific hotel with available transport options

requires authentication

Synchronously fetches all offers for the specified hotel(s) with grouping by flights and buses. Results are cached for a short period (configurable TTL) for fast repeated access. This endpoint is typically used on hotel detail pages to show all available offers and transport options for filtering on the frontend.

POST /api/packages/hotel-offers

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/hotel-offers"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "departure_id": 202553,
    "destination_id": 269,
    "departure_date": "2025-11-28",
    "nights": 7,
    "transport": [
        "flight",
        "bus"
    ],
    "hotel_ids": [
        12,
        34
    ],
    "occupancy": [
        "architecto"
    ],
    "currency": "EUR",
    "provider": "christian_tour",
    "timeout": 30
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/packages/hotel-offers" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"departure_id\": 202553,
    \"destination_id\": 269,
    \"departure_date\": \"2025-11-28\",
    \"nights\": 7,
    \"transport\": [
        \"flight\",
        \"bus\"
    ],
    \"hotel_ids\": [
        12,
        34
    ],
    \"occupancy\": [
        \"architecto\"
    ],
    \"currency\": \"EUR\",
    \"provider\": \"christian_tour\",
    \"timeout\": 30
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/hotel-offers';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'departure_id' => 202553,
            'destination_id' => 269,
            'departure_date' => '2025-11-28',
            'nights' => 7,
            'transport' => [
                'flight',
                'bus',
            ],
            'hotel_ids' => [
                12,
                34,
            ],
            'occupancy' => [
                'architecto',
            ],
            'currency' => 'EUR',
            'provider' => 'christian_tour',
            'timeout' => 30,
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
  "success": true,
  "offers": [...],
  "transport_options": {"flights": [...], "buses": [...]},
  "hotel_info": {...},
  "meta": {"total_offers": 42, "providers": ["christian_tour"], "currency": "RON", "cache_ttl_seconds": 600}
}
 

Example response (408):


{
    "success": false,
    "message": "Request timeout. Try with fewer details or larger timeout value."
}
 

Example response (422):


{
  "success": false,
  "message": "Validation failed",
  "errors": {...}
}
 

Example response (500):


{
    "success": false,
    "message": "Failed to fetch hotel offers"
}
 

Request      

POST api/packages/hotel-offers

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

departure_id   integer   

Departure location ID. Example: 202553

destination_id   integer  optional  

optional Destination location ID (optional when hotel_ids provided). Example: 269

departure_date   string   

Departure date. Must be after today. Example: 2025-11-28

nights   integer   

Number of nights. Min: 1, Max: 30. Example: 7

transport   string[]  optional  

optional Array of transport types. Default: ["flight", "bus"].

*   string  optional  

Transport type. Values: bus, flight. Example: flight

hotel_ids   string[]   

Array of hotel IDs to get offers for.

*   integer  optional  

Hotel ID. Example: 16

occupancy   string[]   

Room occupancy information. Min: 1 room.

*   object  optional  
adults   integer   

Number of adults. Min: 1, Max: 10. Example: 2

children_ages   string[]  optional  

optional Ages of children. Max: 10 children.

*   integer  optional  

Child age. Min: 0, Max: 17. Example: 16

currency   string  optional  

optional Currency code. Default: RON. Example: EUR

provider   string  optional  

optional Specific provider to use. If not provided, uses provider from token abilities or first active. Example: christian_tour

timeout   integer  optional  

optional Timeout in seconds (max 60). Default: 30. Example: 30

Package Booking

Book a package offer

requires authentication

Books a travel package offer through ChristianTour provider. Handles complete booking flow including guest validation, pricing verification, and booking confirmation with optional additional services.

POST /api/v1/booking/packages

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/packages"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "offer_id": "\"christian_tour_pkg_456\"",
    "reference_code": "\"REF789123\"",
    "price": "2150.75",
    "currency": "\"EUR\"",
    "occupancy": [
        "architecto"
    ],
    "additional_services": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/booking/packages" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"offer_id\": \"\\\"christian_tour_pkg_456\\\"\",
    \"reference_code\": \"\\\"REF789123\\\"\",
    \"price\": \"2150.75\",
    \"currency\": \"\\\"EUR\\\"\",
    \"occupancy\": [
        \"architecto\"
    ],
    \"additional_services\": []
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/packages';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'offer_id' => '"christian_tour_pkg_456"',
            'reference_code' => '"REF789123"',
            'price' => '2150.75',
            'currency' => '"EUR"',
            'occupancy' => [
                'architecto',
            ],
            'additional_services' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "order_id": "CT_PKG_ORD_789456",
        "booking_confirmation_number": "CT_PKG_BKG_123789",
        "status": "confirmed",
        "offer_id": "christian_tour_pkg_456",
        "package_name": "7 Days Rome & Florence",
        "departure_date": "2024-06-15",
        "return_date": "2024-06-22",
        "nights": 7,
        "total_price": 2150.75,
        "currency": "EUR",
        "travelers": [
            {
                "first_name": "Maria",
                "last_name": "Garcia",
                "type": "adult",
                "room_assignment": "Room 1"
            }
        ],
        "included_services": [
            "Hotel accommodation",
            "Daily breakfast",
            "Transportation"
        ],
        "additional_services": [
            {
                "code": "EXCURSION_ROME",
                "name": "Rome City Tour",
                "price": 85,
                "currency": "EUR"
            }
        ],
        "booking_date": "2024-01-15T10:30:00Z",
        "cancellation_policy": {
            "free_cancellation_until": "2024-05-15T23:59:59Z",
            "penalty_schedule": [
                {
                    "from_date": "2024-05-16T00:00:00Z",
                    "to_date": "2024-06-01T23:59:59Z",
                    "penalty_percentage": 25
                }
            ]
        }
    },
    "meta": {
        "booking_type": "packages",
        "provider": "christian_tour",
        "total_travelers": 1,
        "total_rooms": 1,
        "processing_time": "2024-01-15T10:30:15Z"
    }
}
 

Example response (400):


{
    "success": false,
    "error": {
        "message": "Package offer no longer available",
        "code": "OFFER_UNAVAILABLE",
        "provider": "christian_tour",
        "offer_id": "christian_tour_pkg_456"
    }
}
 

Example response (422):


{
    "success": false,
    "error": {
        "message": "Validation failed",
        "details": {
            "offer_id": [
                "The offer id field is required."
            ],
            "occupancy.0.guests.0.birth_date": [
                "The birth date does not match the format Y-m-d."
            ]
        },
        "code": "VALIDATION_ERROR"
    }
}
 

Example response (500):


{
    "success": false,
    "error": {
        "message": "An unexpected error occurred while processing the booking",
        "code": "INTERNAL_ERROR"
    }
}
 

Request      

POST api/booking/packages

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

offer_id   string   

The package offer identifier from search results. Example: "christian_tour_pkg_456"

reference_code   string  optional  

optional Internal reference code for tracking. Example: "REF789123"

price   numeric   

Total package price. Min: 0. Example: 2150.75

currency   string   

3-letter currency code. Example: "EUR"

occupancy   string[]   

Room and traveler information. Min: 1 room.

*   object  optional  
room_code   string   

Room type code. Example: "DBL"

guests   string[]   

Guest information. Min: 1 guest.

*   object  optional  
first_name   string   

Guest first name. Max: 255 chars. Example: "Maria"

last_name   string   

Guest last name. Max: 255 chars. Example: "Garcia"

birth_date   string   

Guest birth date. Format: Y-m-d. Example: "1990-05-20"

gender   string   

Guest gender. Values: m, f. Example: "f"

additional_services   string[]  optional  

optional Array of additional service codes.

*   string  optional  

Additional service code from package customization. Example: architecto

Verify a package offer before booking

requires authentication

Verifies package offer availability and current pricing through ChristianTour. Ensures offer is still valid and price hasn't changed before booking.

GET /api/v1/booking/packages/verify?offer_id=xxx

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/packages/verify"
);

const params = {
    "offer_id": ""christian_tour_pkg_456"",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/booking/packages/verify?offer_id=%22christian_tour_pkg_456%22" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/packages/verify';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'offer_id' => '"christian_tour_pkg_456"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "available": true,
        "offer_id": "christian_tour_pkg_456",
        "package_name": "7 Days Rome & Florence",
        "original_price": 2150.75,
        "current_price": 2150.75,
        "price_changed": false,
        "currency": "EUR",
        "valid_until": "2024-01-15T23:59:59Z",
        "departure_date": "2024-06-15",
        "availability": {
            "seats_available": 8,
            "rooms_available": 5
        },
        "last_verified": "2024-01-15T10:30:00Z"
    },
    "meta": {
        "offer_type": "packages",
        "provider": "christian_tour",
        "verified_at": "2024-01-15T10:30:00Z"
    }
}
 

Example response (400):


{
    "success": false,
    "error": {
        "message": "Package offer no longer available",
        "code": "OFFER_VERIFICATION_ERROR",
        "offer_id": "christian_tour_pkg_456"
    }
}
 

Example response (422):


{
    "success": false,
    "error": {
        "message": "Validation failed",
        "details": {
            "offer_id": [
                "The offer id field is required."
            ]
        },
        "code": "VALIDATION_ERROR"
    }
}
 

Example response (500):


{
    "success": false,
    "error": {
        "message": "Failed to verify package offer",
        "details": "Provider connection timeout",
        "code": "VERIFICATION_ERROR"
    }
}
 

Request      

GET api/booking/packages/verify

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

offer_id   string   

The package offer ID to verify. Example: "christian_tour_pkg_456"

Customize a package offer by adding optional services

requires authentication

Adds optional services to a package offer and returns updated pricing. Services can include excursions, transfers, insurance, and upgrades.

POST /api/v1/booking/packages/customize

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/packages/customize"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "offer_id": "\"christian_tour_pkg_456\"",
    "service_codes": [
        "architecto"
    ]
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/booking/packages/customize" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"offer_id\": \"\\\"christian_tour_pkg_456\\\"\",
    \"service_codes\": [
        \"architecto\"
    ]
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/packages/customize';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'offer_id' => '"christian_tour_pkg_456"',
            'service_codes' => [
                'architecto',
            ],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "offer_id": "christian_tour_pkg_456",
        "package_name": "7 Days Rome & Florence",
        "original_price": 2150.75,
        "additional_services_price": 185,
        "total_price": 2335.75,
        "currency": "EUR",
        "added_services": [
            {
                "code": "EXCURSION_ROME",
                "name": "Rome City Tour",
                "description": "Full day guided tour of Rome's historic sites",
                "price": 85,
                "currency": "EUR",
                "duration": "8 hours",
                "included": true
            }
        ],
        "pricing": {
            "base_price": 2150.75,
            "services_total": 185,
            "selling_price": 2335.75,
            "currency": "EUR"
        },
        "valid_until": "2024-01-15T23:59:59Z"
    },
    "meta": {
        "offer_type": "packages",
        "provider": "christian_tour",
        "customized_at": "2024-01-15T10:30:00Z",
        "added_services_count": 1
    }
}
 

Example response (422):


{
    "success": false,
    "error": {
        "message": "Validation failed",
        "details": {
            "offer_id": [
                "The offer id field is required."
            ],
            "service_codes": [
                "The service codes field is required."
            ]
        },
        "code": "VALIDATION_ERROR"
    }
}
 

Example response (500):


{
    "success": false,
    "error": {
        "message": "Failed to customize package offer",
        "details": "Service customization error",
        "code": "CUSTOMIZATION_ERROR"
    }
}
 

Request      

POST api/booking/packages/customize

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

offer_id   string   

The package offer ID to customize. Example: "christian_tour_pkg_456"

service_codes   string[]   

Array of service codes to add. Min: 1 service.

*   string   

Service code from available options. Example: "EXCURSION_ROME"

Get cancellation policy for a package offer

requires authentication

Retrieves the cancellation policy for a specific package offer, including free cancellation periods and penalty schedules.

GET /api/v1/booking/packages/cancellation-policy?offer_id=xxx

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/packages/cancellation-policy"
);

const params = {
    "offer_id": ""christian_tour_pkg_456"",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/booking/packages/cancellation-policy?offer_id=%22christian_tour_pkg_456%22" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/packages/cancellation-policy';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'offer_id' => '"christian_tour_pkg_456"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "offer_id": "christian_tour_pkg_456",
        "package_name": "7 Days Rome & Florence",
        "cancellation_policy": {
            "free_cancellation_until": "2024-05-15T23:59:59Z",
            "penalty_schedule": [
                {
                    "from_date": "2024-05-16T00:00:00Z",
                    "to_date": "2024-06-01T23:59:59Z",
                    "penalty_percentage": 25,
                    "penalty_description": "25% of total booking value"
                },
                {
                    "from_date": "2024-06-02T00:00:00Z",
                    "to_date": "2024-06-14T23:59:59Z",
                    "penalty_percentage": 50,
                    "penalty_description": "50% of total booking value"
                },
                {
                    "from_date": "2024-06-15T00:00:00Z",
                    "penalty_percentage": 100,
                    "penalty_description": "No refund"
                }
            ],
            "general_terms": [
                "Cancellation must be requested in writing",
                "Refunds processed within 14 business days",
                "Force majeure exceptions may apply"
            ],
            "contact_info": {
                "email": "cancellations@christian-tour.com",
                "phone": "+40 21 123 4567"
            }
        },
        "departure_date": "2024-06-15",
        "current_date": "2024-01-15"
    },
    "meta": {
        "offer_type": "packages",
        "provider": "christian_tour",
        "retrieved_at": "2024-01-15T10:30:00Z"
    }
}
 

Example response (404):


{
    "success": false,
    "error": {
        "message": "Package offer not found",
        "code": "OFFER_NOT_FOUND",
        "offer_id": "christian_tour_pkg_456"
    }
}
 

Example response (422):


{
    "success": false,
    "error": {
        "message": "Validation failed",
        "details": {
            "offer_id": [
                "The offer id field is required."
            ]
        },
        "code": "VALIDATION_ERROR"
    }
}
 

Example response (500):


{
    "success": false,
    "error": {
        "message": "Failed to retrieve cancellation policy",
        "details": "Provider connection timeout",
        "code": "POLICY_ERROR"
    }
}
 

Request      

GET api/booking/packages/cancellation-policy

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

offer_id   string   

The package offer ID to get policy for. Example: "christian_tour_pkg_456"

Get package booking statistics

requires authentication

Retrieves comprehensive booking statistics for package bookings, including success rates, average values, and trends.

GET /api/v1/booking/packages/stats

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/packages/stats"
);

const params = {
    "period": ""month"",
    "from_date": ""2024-01-01"",
    "to_date": ""2024-01-31"",
};
Object.keys(params)
    .forEach(key => url.searchParams.append(key, params[key]));

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/booking/packages/stats?period=%22month%22&from_date=%222024-01-01%22&to_date=%222024-01-31%22" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/packages/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'query' => [
            'period' => '"month"',
            'from_date' => '"2024-01-01"',
            'to_date' => '"2024-01-31"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "period": "month",
        "from_date": "2024-01-01",
        "to_date": "2024-01-31",
        "summary": {
            "total_bookings": 156,
            "successful_bookings": 142,
            "failed_bookings": 14,
            "success_rate": 91.03,
            "total_value": 334250.75,
            "average_booking_value": 2143.27,
            "currency": "EUR"
        },
        "breakdown_by_status": {
            "confirmed": 135,
            "on_request": 7,
            "cancelled": 9,
            "failed": 5
        },
        "top_destinations": [
            {
                "destination": "Rome & Florence",
                "bookings": 45,
                "total_value": 98450.25
            },
            {
                "destination": "Paris & Amsterdam",
                "bookings": 32,
                "total_value": 76240.5
            }
        ],
        "booking_trends": [
            {
                "date": "2024-01-01",
                "bookings": 5,
                "value": 10725.5
            },
            {
                "date": "2024-01-02",
                "bookings": 8,
                "value": 17440.25
            }
        ],
        "error_analysis": {
            "most_common_errors": [
                {
                    "error_type": "OFFER_UNAVAILABLE",
                    "count": 8,
                    "percentage": 57.14
                },
                {
                    "error_type": "PAYMENT_FAILED",
                    "count": 4,
                    "percentage": 28.57
                }
            ]
        }
    },
    "meta": {
        "booking_type": "packages",
        "provider": "christian_tour",
        "generated_at": "2024-01-15T10:30:00Z",
        "timezone": "UTC"
    }
}
 

Example response (422):


{
    "success": false,
    "error": {
        "message": "Validation failed",
        "details": {
            "period": [
                "The selected period is invalid."
            ],
            "from_date": [
                "The from date does not match the format Y-m-d."
            ]
        },
        "code": "VALIDATION_ERROR"
    }
}
 

Example response (500):


{
    "success": false,
    "error": {
        "message": "Failed to retrieve booking statistics",
        "details": "Database query timeout",
        "code": "STATS_ERROR"
    }
}
 

Request      

GET api/booking/packages/stats

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Query Parameters

period   string  optional  

optional Statistics period. Values: day, week, month, year. Default: month. Example: "month"

from_date   string  optional  

optional Start date for custom period. Format: Y-m-d. Example: "2024-01-01"

to_date   string  optional  

optional End date for custom period. Format: Y-m-d. Example: "2024-01-31"

Hotel Booking

Book a hotel offer

requires authentication

Books a hotel offer through various travel providers (primarily ChristianTour). Requires guest information for all rooms and handles validation, pricing verification, and booking confirmation through the travel booking service.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/hotels"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "offer_id": "\"christian_tour_offer_123\"",
    "price": "1250.50",
    "currency": "\"EUR\"",
    "occupancy": [
        "architecto"
    ],
    "reference_code": "\"REF123456\"",
    "booking_reference": "\"christian_tour:hotels:2\"",
    "provider": "\"christian_tour\""
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/booking/hotels" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"offer_id\": \"\\\"christian_tour_offer_123\\\"\",
    \"price\": \"1250.50\",
    \"currency\": \"\\\"EUR\\\"\",
    \"occupancy\": [
        \"architecto\"
    ],
    \"reference_code\": \"\\\"REF123456\\\"\",
    \"booking_reference\": \"\\\"christian_tour:hotels:2\\\"\",
    \"provider\": \"\\\"christian_tour\\\"\"
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/hotels';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'offer_id' => '"christian_tour_offer_123"',
            'price' => '1250.50',
            'currency' => '"EUR"',
            'occupancy' => [
                'architecto',
            ],
            'reference_code' => '"REF123456"',
            'booking_reference' => '"christian_tour:hotels:2"',
            'provider' => '"christian_tour"',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (201):


{
    "success": true,
    "data": {
        "order_id": "CT_ORD_789123",
        "booking_confirmation_number": "CT_BKG_456789",
        "status": "confirmed",
        "offer_id": "christian_tour_offer_123",
        "hotel_name": "Grand Hotel Milan",
        "check_in": "2024-06-15",
        "check_out": "2024-06-22",
        "nights": 7,
        "total_price": 1250.5,
        "currency": "EUR",
        "guests": [
            {
                "first_name": "John",
                "last_name": "Smith",
                "room_assignment": "Room 1"
            }
        ],
        "booking_date": "2024-01-15T10:30:00Z",
        "cancellation_policy": {
            "free_cancellation_until": "2024-06-08T23:59:59Z",
            "penalty_after": "50% of total price"
        }
    }
}
 

Example response (400):


{
    "success": false,
    "error": {
        "message": "Offer no longer available",
        "code": "OFFER_UNAVAILABLE",
        "provider": "christian_tour",
        "offer_id": "christian_tour_offer_123"
    }
}
 

Example response (422):


{
    "success": false,
    "error": "validation_failed",
    "message": "Validation failed",
    "errors": {
        "offer_id": [
            "The offer id field is required."
        ],
        "occupancy.0.guests.0.birth_date": [
            "The birth date does not match the format Y-m-d."
        ]
    }
}
 

Example response (500):


{
    "success": false,
    "error": "internal_error",
    "message": "An unexpected error occurred"
}
 

Request      

POST api/booking/hotels

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

offer_id   string   

The unique offer identifier from search results. Example: "christian_tour_offer_123"

price   numeric   

The total offer price. Min: 0. Example: 1250.50

currency   string   

3-letter currency code. Example: "EUR"

occupancy   string[]   

Room and guest information. Min: 1 room.

*   object  optional  
room_code   string   

Room code from the offer. Example: "DBL"

guests   string[]   

Guest information. Min: 1 guest.

*   object  optional  
first_name   string   

Guest first name. Example: "John"

last_name   string   

Guest last name. Example: "Smith"

birth_date   string   

Guest birth date. Format: Y-m-d. Example: "1985-03-15"

gender   string   

Guest gender. Values: m, f. Example: "m"

reference_code   string  optional  

optional Internal reference code. Example: "REF123456"

booking_reference   string  optional  

optional Booking reference in format provider:entity_type:external_id. Example: "christian_tour:hotels:2"

provider   string  optional  

optional Provider name if booking_reference not provided. Example: "christian_tour"

Verify a hotel offer before booking

requires authentication

Verifies if a hotel offer is still available and at the same price before proceeding with booking. This ensures price accuracy and availability.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/hotels/verify-offer"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "provider": "\"christian_tour\"",
    "offer_id": "\"christian_tour_offer_123\"",
    "search_params": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/booking/hotels/verify-offer" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"provider\": \"\\\"christian_tour\\\"\",
    \"offer_id\": \"\\\"christian_tour_offer_123\\\"\",
    \"search_params\": []
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/hotels/verify-offer';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'provider' => '"christian_tour"',
            'offer_id' => '"christian_tour_offer_123"',
            'search_params' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "available": true,
        "offer_id": "christian_tour_offer_123",
        "hotel_name": "Grand Hotel Milan",
        "original_price": 1250.5,
        "current_price": 1250.5,
        "price_changed": false,
        "currency": "EUR",
        "valid_until": "2024-01-15T23:59:59Z",
        "rooms_available": 3,
        "last_verified": "2024-01-15T10:30:00Z"
    }
}
 

Example response (400):


{
    "success": false,
    "error": {
        "message": "Offer no longer available",
        "code": "OFFER_UNAVAILABLE",
        "provider": "christian_tour",
        "offer_id": "christian_tour_offer_123",
        "reason": "Sold out"
    }
}
 

Example response (422):


{
    "success": false,
    "error": "validation_failed",
    "message": "Validation failed",
    "errors": {
        "provider": [
            "The provider field is required."
        ],
        "offer_id": [
            "The offer id field is required."
        ]
    }
}
 

Example response (500):


{
    "success": false,
    "error": "verification_failed",
    "message": "Failed to verify offer: Provider connection timeout"
}
 

Request      

POST api/booking/hotels/verify-offer

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

provider   string   

Provider name. Example: "christian_tour"

offer_id   string   

The offer ID to verify. Example: "christian_tour_offer_123"

search_params   string[]  optional  

optional Original search parameters used.

Verify a hotel offer before booking

requires authentication

Verifies if a hotel offer is still available and at the same price before proceeding with booking. This ensures price accuracy and availability.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/booking/verify-offer"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

let body = {
    "provider": "\"christian_tour\"",
    "offer_id": "\"christian_tour_offer_123\"",
    "search_params": []
};

fetch(url, {
    method: "POST",
    headers,
    body: JSON.stringify(body),
}).then(response => response.json());
curl --request POST \
    "https://apisun.trip.ro/api/booking/verify-offer" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json" \
    --data "{
    \"provider\": \"\\\"christian_tour\\\"\",
    \"offer_id\": \"\\\"christian_tour_offer_123\\\"\",
    \"search_params\": []
}"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/booking/verify-offer';
$response = $client->post(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
        'json' => [
            'provider' => '"christian_tour"',
            'offer_id' => '"christian_tour_offer_123"',
            'search_params' => [],
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "available": true,
        "offer_id": "christian_tour_offer_123",
        "hotel_name": "Grand Hotel Milan",
        "original_price": 1250.5,
        "current_price": 1250.5,
        "price_changed": false,
        "currency": "EUR",
        "valid_until": "2024-01-15T23:59:59Z",
        "rooms_available": 3,
        "last_verified": "2024-01-15T10:30:00Z"
    }
}
 

Example response (400):


{
    "success": false,
    "error": {
        "message": "Offer no longer available",
        "code": "OFFER_UNAVAILABLE",
        "provider": "christian_tour",
        "offer_id": "christian_tour_offer_123",
        "reason": "Sold out"
    }
}
 

Example response (422):


{
    "success": false,
    "error": "validation_failed",
    "message": "Validation failed",
    "errors": {
        "provider": [
            "The provider field is required."
        ],
        "offer_id": [
            "The offer id field is required."
        ]
    }
}
 

Example response (500):


{
    "success": false,
    "error": "verification_failed",
    "message": "Failed to verify offer: Provider connection timeout"
}
 

Request      

POST api/booking/verify-offer

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Body Parameters

provider   string   

Provider name. Example: "christian_tour"

offer_id   string   

The offer ID to verify. Example: "christian_tour_offer_123"

search_params   string[]  optional  

optional Original search parameters used.

Travel Data

Get hotel details by external_id

requires authentication

Retrieves detailed information about a specific hotel by its external_id (christian_tour). Includes related destination information, booking reference, and facilities.

Facilities are loaded from the normalized travel_hotel_facilities table, providing up-to-date amenity information from the provider.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/packages/hotels/"2""
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/packages/hotels/"2"" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/packages/hotels/"2"';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "id": 2,
        "destination_id": 234,
        "name": "Grand Hotel Milan",
        "type": "hotel",
        "classification": 5,
        "latitude": 45.46427,
        "longitude": 9.18951,
        "description": "Luxury hotel in Milan center",
        "address": {
            "street": "Via del Corso 123",
            "city": "Milan",
            "country": "Italy",
            "postal_code": "20121"
        },
        "images": [
            {
                "url": "https://example.com/hotel1.jpg",
                "type": "exterior"
            },
            {
                "url": "https://example.com/hotel2.jpg",
                "type": "room"
            }
        ],
        "facilities": [
            {
                "id": 1,
                "name": "Meniu masă pentru copii"
            },
            {
                "id": 8,
                "name": "Restaurant"
            },
            {
                "id": 11,
                "name": "Bar"
            },
            {
                "id": 24,
                "name": "Servicii de înfrumuseΘ›are"
            },
            {
                "id": 39,
                "name": "Saună"
            }
        ],
        "giata_id": "12345",
        "provider": "christian_tour",
        "external_id": "2",
        "booking_reference": "christian_tour:hotels:2",
        "last_synced_at": "2025-10-20T09:30:00.000000Z",
        "data_freshness": "fresh",
        "destination": {
            "id": 234,
            "name": "Milan",
            "type": "destination",
            "country_code": "IT",
            "latitude": 45.46427,
            "longitude": 9.18951,
            "provider": "christian_tour",
            "external_id": "234",
            "booking_reference": "christian_tour:destinations:234",
            "last_synced_at": "2025-10-20T08:00:00.000000Z",
            "data_freshness": "fresh"
        }
    },
    "meta": {
        "cached": false,
        "cache_key": "hotel_details:christian_tour:2",
        "cache_ttl": 3600
    }
}
 

Example response (404):


{
    "error": "Hotel not found",
    "message": "Hotel with external_id '2' not found"
}
 

Request      

GET api/packages/hotels/{id}

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

URL Parameters

id   string   

The hotel external_id from provider. Example: "2"

System Status

Get detailed health check of Travel system

requires authentication

Provides comprehensive health check including database connectivity, provider status, sync status, and cache health.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/travel/health"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/travel/health" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/travel/health';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "overall_status": "healthy",
        "database": {
            "status": "healthy",
            "connection": "active",
            "tables": {
                "travel_destinations": "exists",
                "travel_hotels": "exists"
            }
        },
        "providers": {
            "christian_tour": {
                "status": "healthy",
                "response_time_ms": 245
            }
        },
        "sync_status": {
            "last_sync": "2024-01-15T10:30:00Z",
            "status": "healthy",
            "pending_syncs": 0
        },
        "cache": {
            "status": "healthy",
            "hit_ratio": 87,
            "size_mb": 245
        }
    }
}
 

Example response (503):


{
    "success": true,
    "data": {
        "overall_status": "unhealthy",
        "database": {
            "status": "error",
            "error": "Connection timeout"
        }
    }
}
 

Request      

GET api/travel/health

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Get provider statistics

requires authentication

Retrieve comprehensive statistics about all travel data providers including their status, data counts, sync information, and performance metrics.

This endpoint provides detailed insights into the health and performance of each provider in the travel API system, including:

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/travel/providers/stats"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/travel/providers/stats" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/travel/providers/stats';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": [
        {
            "provider": "christian_tour",
            "active": true,
            "status": "healthy",
            "last_sync": "2024-01-15T10:30:00Z",
            "sync_status": "success",
            "data_counts": {
                "destinations": 1234,
                "hotels": 5678,
                "circuits": 234,
                "experiences": 456,
                "packages": 789
            },
            "performance": {
                "avg_response_time_ms": 85,
                "success_rate": 99.5,
                "error_count": 2,
                "cache_hit_ratio": 87.3
            },
            "sync_info": {
                "last_full_sync": "2024-01-15T06:00:00Z",
                "next_scheduled_sync": "2024-01-15T14:00:00Z",
                "sync_frequency_hours": 8
            }
        },
        {
            "provider": "obs",
            "active": true,
            "status": "warning",
            "last_sync": "2024-01-15T08:45:00Z",
            "sync_status": "partial_error",
            "data_counts": {
                "destinations": 856,
                "hotels": 2345,
                "circuits": 123,
                "experiences": 234,
                "packages": 567
            },
            "performance": {
                "avg_response_time_ms": 120,
                "success_rate": 95.2,
                "error_count": 15,
                "cache_hit_ratio": 78.9
            },
            "sync_info": {
                "last_full_sync": "2024-01-15T02:00:00Z",
                "next_scheduled_sync": "2024-01-15T10:00:00Z",
                "sync_frequency_hours": 8
            }
        }
    ],
    "meta": {
        "active_providers": 2,
        "total_providers": 2,
        "generated_at": "2024-01-15T12:00:00Z"
    }
}
 

Example response (500):


{
    "error": "Internal server error",
    "message": "Failed to retrieve provider statistics"
}
 

Request      

GET api/travel/providers/stats

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Get detailed health check of Travel system

requires authentication

Provides comprehensive health check including database connectivity, provider status, sync status, and cache health.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/health"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/health" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/health';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "overall_status": "healthy",
        "database": {
            "status": "healthy",
            "connection": "active",
            "tables": {
                "travel_destinations": "exists",
                "travel_hotels": "exists"
            }
        },
        "providers": {
            "christian_tour": {
                "status": "healthy",
                "response_time_ms": 245
            }
        },
        "sync_status": {
            "last_sync": "2024-01-15T10:30:00Z",
            "status": "healthy",
            "pending_syncs": 0
        },
        "cache": {
            "status": "healthy",
            "hit_ratio": 87,
            "size_mb": 245
        }
    }
}
 

Example response (503):


{
    "success": true,
    "data": {
        "overall_status": "unhealthy",
        "database": {
            "status": "error",
            "error": "Connection timeout"
        }
    }
}
 

Request      

GET api/health

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json

Get Travel system status and sync information

requires authentication

Provides detailed status information about providers, data types, and synchronization status.

Example request:
const url = new URL(
    "https://apisun.trip.ro/api/status"
);

const headers = {
    "Authorization": "Bearer {YOUR_AUTH_KEY}",
    "Content-Type": "application/json",
    "Accept": "application/json",
};

fetch(url, {
    method: "GET",
    headers,
}).then(response => response.json());
curl --request GET \
    --get "https://apisun.trip.ro/api/status" \
    --header "Authorization: Bearer {YOUR_AUTH_KEY}" \
    --header "Content-Type: application/json" \
    --header "Accept: application/json"
$client = new \GuzzleHttp\Client();
$url = 'https://apisun.trip.ro/api/status';
$response = $client->get(
    $url,
    [
        'headers' => [
            'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
            'Content-Type' => 'application/json',
            'Accept' => 'application/json',
        ],
    ]
);
$body = $response->getBody();
print_r(json_decode((string) $body));

Example response (200):


{
    "success": true,
    "data": {
        "providers": {
            "christian_tour": {
                "name": "christian_tour",
                "last_sync": "2024-01-15T10:30:00Z",
                "sync_status": "success",
                "total_records": 1234,
                "freshness": "fresh"
            }
        },
        "data_types": {
            "destinations": {
                "total_records": 2090,
                "last_sync": "2024-01-15T10:30:00Z",
                "freshness": "fresh"
            },
            "hotels": {
                "total_records": 5432,
                "last_sync": "2024-01-15T09:45:00Z",
                "freshness": "acceptable"
            }
        },
        "database_stats": {
            "total_records": 7756,
            "active_providers": 2
        }
    }
}
 

Request      

GET api/status

Headers

Authorization      

Example: Bearer {YOUR_AUTH_KEY}

Content-Type      

Example: application/json

Accept      

Example: application/json