Booking Details API
Authenticated endpoint for retrieving comprehensive booking details including offer, product, and passenger information.
Overview
Section titled “Overview”The Booking Details API provides an admin-only endpoint for accessing complete booking information scoped to specific markets and languages.
Authentication: Requires active admin session (web middleware + auth) and admin role.
Base URL Pattern: /api/{market}/{lang}/bookings/{bookingReference}
Endpoint
Section titled “Endpoint”GET /api/{market}/{lang}/bookings/{bookingReference}
Section titled “GET /api/{market}/{lang}/bookings/{bookingReference}”Get complete booking details by reference code.
Parameters:
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| market | path | string | Yes | Market code (case-insensitive) |
| lang | path | string | Yes | Language code (e.g., “en”, “es”, “ca”) |
| bookingReference | path | string | Yes | Booking reference code (e.g., “BK-ABC12345”) |
Response: 200 OK
{ "data": { "booking_reference": "BK-ABC12345", "status": "in_progress", "status_label": "In Progress", "total_amount": "2500.00", "deposit_amount": "980.00", "currency": "EUR", "currency_symbol": "€", "booked_at": "2024-12-15T10:30:00+00:00", "number_of_travelers": 2, "market_code": "es", "customer_service_phone": "+34 919 49 45 52", "footer_copyright": "© BY VOLARE SL. Todos los derechos reservados", "offer": { "id": 123, "sku": "ES-5CMB10-CA1-2024-01-15", "departure_date": "2024-01-15", "return_date": "2024-01-26", "trip_days": 12, "final_price": "1300.00", "marketing_price_per_pax": "650.00", "room_type": "double", "room_type_label": "Double Room", "departure_airport": { "iata_code": "BCN", "name": "Barcelona-El Prat Airport", "city": "Barcelona" } }, "product": { "id": 10, "sku": "ES-5CMB10-CA1", "title": "Tour de Sri Lanka", "subtitle": "Descobreix l'illa maragda", "short_description": "Una aventura increible...", "highlights": ["Sigiriya", "Kandy", "Yala"], "itinerary": [ { "day": 1, "date": "2024-01-15", "title": "Arrival in Colombo", "details": "Arrive, meet your host, private transfer to the hotel.", "image_url": "https://cdn.example.com/itineraries/day1.jpg", "is_flight_day": false } ], "hero_image": "https://cdn.example.com/images/sri-lanka.jpg", "trip_duration_days": 10 }, "passengers": [ { "id": 1, "first_name": "John", "last_name": "Doe", "full_name": "John Doe", "age_category": "adult", "date_of_birth": "1985-03-15", "is_lead_passenger": true, "email": "john@example.com", "phone": "+34612345678" } ], "hotels": [ { "name": "Cinnamon Lodge Habarana", "city": "Habarana", "nights": 2, "check_in": "2024-01-15", "check_out": "2024-01-17", "image_urls": ["https://cdn.example.com/hotels/cinnamon.jpg"], "is_upgrade": false } ], "flights": { "cabin_class": "ECONOMY", "outbound": { "date": "2024-01-15", "departure_time": "10:30", "arrival_time": "23:45", "departure_airport": { "iata_code": "BCN", "city": "Barcelona" }, "arrival_airport": { "iata_code": "CMB", "city": "Colombo" }, "segments": [ { "flight_number": "EK186", "airline_code": "EK", "airline_name": "Emirates", "departure_airport": { "iata_code": "BCN", "city": "Barcelona" }, "arrival_airport": { "iata_code": "DXB", "city": "Dubai" }, "departure_time": "10:30", "arrival_time": "20:15" } ], "stopovers": [ { "airport": { "iata_code": "DXB", "city": "Dubai" }, "layover_minutes": 120 } ], "total_duration_minutes": 795 }, "inbound": { "date": "2024-01-25", "departure_time": "01:30", "arrival_time": "12:15", "departure_airport": { "iata_code": "CMB", "city": "Colombo" }, "arrival_airport": { "iata_code": "BCN", "city": "Barcelona" }, "segments": [], "stopovers": [], "total_duration_minutes": 780 } }, "experiences": [ { "name": "Visita guiada a la roca de Sigiriya", "city": "Sigiriya", "date": "2024-01-16", "image_url": "https://cdn.example.com/activities/sigiriya.jpg", "tier": "included" }, { "name": "Safari en jeep por el Parque Nacional de Yala", "city": "Yala", "date": "2024-01-17", "image_url": "https://cdn.example.com/activities/yala-safari.jpg", "tier": "extra" } ], "optional_services": [ { "name": "Traslado privado aeropuerto - hotel", "description": "Vehículo privado con conductor desde el aeropuerto de Colombo hasta el hotel." } ], "section_prices": { "hotels": 0.0, "experiences": 120.0, "services": 60.0 } }}Error Responses
Section titled “Error Responses”Not Authenticated (401)
Section titled “Not Authenticated (401)”{ "message": "Unauthenticated."}Not Admin (403)
Section titled “Not Admin (403)”{ "message": "Access denied. Admin role required."}Booking Not Found (404)
Section titled “Booking Not Found (404)”Returned when booking does not exist, or does not belong to requested market/locale.
{ "success": false, "error": "booking_not_found", "message": "Booking with reference 'XYZ' not found."}Language Not Supported (400)
Section titled “Language Not Supported (400)”{ "success": false, "error": "language_not_supported", "message": "Language 'de' is not supported by market 'ES'. Supported languages: es, ca"}Flight Data Sources
Section titled “Flight Data Sources”The flights field is populated from multiple sources in priority order:
| Priority | Source | Cabin Class | When Used |
|---|---|---|---|
| 1 | booking_upsells.flight_search_params |
BUSINESS | Customer upgraded to business class |
| 2 | booking.flight_selection |
ECONOMY | Customer selected economy flight during checkout |
| 3 | dynamic_flight_cache (itinerary_index=1) |
ECONOMY | Legacy fallback for older bookings |
The resource handles both enriched format (with nested airport objects containing iata_code and city) and legacy format (plain IATA code strings) for backward compatibility.
Each segment carries both airline_code (IATA code, e.g. EK) and airline_name (full carrier name, e.g. Emirates). For stored economy selections and business-upgrade legs, the airline is resolved per segment from the segments[*] entries (one per flight number), falling back to the leg’s distinct airlines/airline_names arrays by index and finally to the flight-number prefix. The per-segment source matters on multi-segment same-airline legs, where indexing the distinct arrays would mislabel later segments (e.g. flight 907 shown as 90). Cached economy legs resolve the name from the dynamic_flight_cache_segments.airline_name column, falling back to the operating carrier code.
Trip Dates (Door-to-Door)
Section titled “Trip Dates (Door-to-Door)”offer.return_date (YYYY-MM-DD) and offer.trip_days (inclusive day count) are derived from the bound international flight via Offer::getTravelDates() — door-to-door, outbound departure day to return arrival-home day (same source as the booking emails). Both are null when no flight is bound (land-only / pre-flight); the frontend then falls back to the product.trip_duration_days land-tour estimate. product.trip_duration_days (itinerary nights + 1) remains unchanged and can under-report long-haul trips whose return leg spans an extra travel day.
Top-Level Fields
Section titled “Top-Level Fields”In addition to the booking core fields, the response includes market-aware metadata used by the frontend:
| Field | Type | Description |
|---|---|---|
market_code |
string | Lowercased market code for frontend routing (defaults to es) |
customer_service_phone |
string | null | Market customer-service phone number |
footer_copyright |
string | null | Footer copyright text, resolved from CmsFooter via the market’s default locale |
Pricing & Status
Section titled “Pricing & Status”The trip page doubles as the quotation page sent to clients. For pre-payment bookings it renders a price + deposit block, so the resource exposes a customer-facing status and a resolved deposit alongside the total.
| Field | Type | Description |
|---|---|---|
status |
string | Customer-facing status (ClientBookingStatus value) — see below |
status_label |
string | Human label for the status (ClientBookingStatus::getLabel()) |
total_amount |
string | Stored decimal (decimal:2) — the booking’s total price |
deposit_amount |
string | null | Resolved deposit (decimal:2 string) — persisted, derived, or null (see below) |
section_prices |
object | Per-section extra totals: { hotels, experiences, services } (floats) |
Customer-facing status
Section titled “Customer-facing status”status is a projection of the operational Booking::$status, computed via
ClientBookingStatus::fromBookingStatus(). The raw operational status (the
flight/land fulfillment pipeline) is never exposed. The client-facing values:
| Value | status_label |
Operational statuses projected onto it |
|---|---|---|
in_progress |
In Progress | Draft, Checkout |
quote |
Quotation | QuotationRequested, QuotationConfirmed |
pending_payment |
Pending Payment | PendingPayment, PaymentProcessing |
confirmed |
Confirmed | PendingFlightBooking, FlightBookingInProgress, FlightBookingFailed, FlightsConfirmed, PendingLandConfirmation, Confirmed, FullyPaid |
balance_due |
Balance Due | AwaitingBalance |
completed |
Completed | Completed |
cancelled |
Cancelled | Cancelled, Expired |
Source: backend/app/Enums/ClientBookingStatus.php.
Deposit resolution
Section titled “Deposit resolution”deposit_amount is resolved by BookingDetailsResource::resolveDepositAmount():
-
Persisted deposit — if the booking has a stored
deposit_amountcolumn (set when payment is initiated), it is emitted verbatim. -
Derived deposit — otherwise, for pre-payment bookings without a persisted deposit (
quoteandin_progress), it is derived from the checkout snapshot viaPaymentCalculatorService::depositFromCheckout():deposit = round(raw_flight_cost + margin% × total_price, 2)deposit = min(deposit, total_price)raw_flight_costcomes fromflight_selection.fare_total_price(business, all legs pax-scaled) or the snapshotflight_base_price(economy). -
Null — any other status with a null persisted deposit stays
null.
pending_payment is deliberately excluded from the derivation set: it always
carries a persisted deposit (set when the booking is promoted to payment), so it
emits the stored value from step 1, never a derived one.
Gotcha: the derived deposit only becomes usable once the checkout snapshot
carries flight pricing. The initial draft snapshot (checkout step flights,
before advancing) only holds step_timestamps, so the derivation yields 0.00
until the booking advances past the flights step.
Source: BookingDetailsResource::resolveDepositAmount() /
deriveDepositFromCheckout(); PaymentCalculatorService::depositFromCheckout().
Section prices
Section titled “Section prices”section_prices aggregates the per-section extra totals shown in the price/quote
view, built by BookingDetailsResource::buildSectionPrices() from
Booking::checkoutExtrasBreakdown(). It works for both quote and finalized
bookings (the breakdown reconciles with the snapshot extras_price). Upsell
types map to the three trip-page sections:
| Key | Upsell types (BookingUpsellType) |
|---|---|
hotels |
Hotel |
experiences |
Activity |
services |
Transfer, FlightUpgrade, Insurance |
Source: BookingDetailsResource::buildSectionPrices().
Hotels
Section titled “Hotels”Each hotel stay groups consecutive nights at the same hotel.
| Field | Type | Description |
|---|---|---|
name |
string | Hotel name |
city |
string | null | POI city name |
nights |
int | Number of nights |
check_in |
string | Check-in date (YYYY-MM-DD) |
check_out |
string | Check-out date (YYYY-MM-DD) |
image_urls |
string[] | Full URLs for all hotel images (empty array when none) |
is_upgrade |
bool | Whether the stay is a selected hotel upgrade (luxury or grand-luxury tier). The highest purchased tier wins: grand-luxury takes precedence over luxury, which takes precedence over the base selection hotel |
Itinerary
Section titled “Itinerary”product.itinerary is a flat, per-day list — one entry per calendar day, sequential from the destination-arrival date. Each item: { day, date, title, details, image_url, is_flight_day }. The tour’s stops (which can span multiple nights) are flattened into individual days: per-day text (title/details) comes from the localized translation, and image_url is resolved from the tour template’s per-day image (day_image), or null when the tour has no image for that day. is_flight_day is false for these content days.
The list may also contain virtual flight-day entries (is_flight_day: true) prepended at the start and/or appended at the end — one per overnight flight night, carrying hardcoded Spanish “en vuelo” copy and an endpoint image. They are dated to the real booked flight (outbound entries to the home-departure date, return entries landing on the home-arrival date). The rendered count per direction is the tour’s authored flight_days (see Product Templates) clamped to the booked flight’s actual span, so an overnight day is never shown for a leg that departs and lands the same calendar day. On a 2-night flight the standard copy is always the first night (Mientras duermes… / El final de un gran viaje, primary image) and the second night is a fixed-title, per-market editable slot (A una película de distancia / Próximamente en los mejores destinos, day-2 image, falling back to the primary when unset) — see Flight-day copy and ordering. When no flight day is rendered, the endpoint’s description/image merges onto the adjacent content day instead.
Source: BookingDetailsResource::buildItineraryData().
Experiences and Optional Services
Section titled “Experiences and Optional Services”These arrays surface the activities and services for the booking:
experiences— the tour’s included activities plus the extra activity upsells the customer added at checkout. Each item:{ name, city, date, image_url, tier }wheretierisincludedorextra. Included activities are derived from the tour itinerary; extras come from activity upsells. Thedateis derived from the booking’s departure date plus the activity’s tour day (nullwhen no day is recorded). Entries are ordered chronologically. (A futuresubstitutiontier is not yet emitted.)optional_services— transfer upsells only (buildServicesDatafilters toBookingUpsellType::Transfer). Each item:{ name, description }. Travel insurance upsells are intentionally not surfaced here; see Travel Insurance (Intermundial).
Activity and transfer names/descriptions are localized to the market’s default locale.
Source Files
Section titled “Source Files”| Component | File |
|---|---|
| Controller | backend/app/Http/Controllers/Api/BookingController.php |
| Booking Resource | backend/app/Http/Resources/BookingDetailsResource.php |
| Offer Resource | backend/app/Http/Resources/OfferSummaryResource.php |
| Passenger Resource | backend/app/Http/Resources/PassengerSummaryResource.php |
| Route | backend/routes/api.php (market-scoped routes) |
Related
Section titled “Related”- Multi-Market API - Market and product endpoints
- Swagger: http://localhost/api/documentation