# Booking detail

> A single booking by its public reference, with the pricing breakdown and the traveler pricing.

Endpoint: `GET /api/v1/bookings/{booking_id}`

Source: https://fx-port.com/docs/account/booking-detail

`GET /api/v1/bookings/{booking_id}`

**Permission:** Read

Returns one booking of your agency, whatever its product: the **summary** of the [history](https://fx-port.com/docs/account/history.md), the
stored **record**, and, for flights and hotels, the **price breakdown** and the **balance operations**; for flights
also the **price per traveler**.

## The reference you can send

`booking_id` is the internal `id` of the booking (a UUID) or the public reference of its product. The reference is
looked up in these fields, in this order, and the first booking found is returned:

| Product | Fields tried |
|---|---|
| `flights` | `booking_id` (the airline record locator, the PNR), the supplier's order id, then `id` |
| `hotels` | `booking_id`, then `id` |
| `esims` | `booking_id` (`FX-` followed by a number), the supplier's order code (`order_id`), the **ICCID** (18 to 22 digits, or a `PENDING-...` placeholder), then `id` |
| `transfers` | `booking_id`, then `id` |

Without `product`, the products are tried in the order flights, hotels, eSIMs, transfers. The reference is at most 100
characters: letters, digits and `._:=+~-` (`400 invalid_booking_id` otherwise).


> [!WARNING]
> **Prefer the internal `id` (UUID).** A PNR or a booking reference is **not globally unique**: the same record locator can
> exist at two airlines or suppliers, so a lookup by PNR can match another booking than the one you mean. The internal `id`
> is in every booking payload, unique and stable: store it, and treat the PNR as a display reference.


**An ICCID returns the original order**, never a later topup. An eSIM that was topped up has several bookings with the same
ICCID; the lookup by ICCID answers the oldest order of type `esim`. Look a topup up by its `booking_id` or its `id`.

## Query parameters

| Parameter | Default | Description |
|---|---|---|
| `product` | `all` | Look in one product only: `flights`, `hotels`, `esims` or `transfers` (case does not matter; the singular names are read as the plural). It is faster, and it avoids a match in another product. A reference that belongs to another product than the one you ask for is `404 booking_not_found` |

Never send `agency_id`: an API key that does is refused with `400 agency_id_not_allowed`.

## Request examples


**cURL**

~~~bash
# Recommended: the internal id (a UUID), which is unambiguous
curl --get \
  --url https://api.fx-port.com/api/v1/bookings/711371c3-00aa-4be2-b790-9f0ca2ccfdb7 \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY' \
  --data-urlencode 'product=flights'

# An eSIM order by its public reference
curl --get \
  --url https://api.fx-port.com/api/v1/bookings/FX-1793546602916 \
  --header 'Authorization: Bearer fxp_test_YOUR_KEY' \
  --data-urlencode 'product=esims'
~~~

**Node.js**

~~~js
const id = "711371c3-00aa-4be2-b790-9f0ca2ccfdb7";
const response = await fetch(`https://api.fx-port.com/api/v1/bookings/${id}?product=flights`, {
  headers: { Authorization: "Bearer fxp_test_YOUR_KEY" },
});
if (response.status === 404) {
  // booking_not_found: not in this environment, or not in this product
}
const { data: booking } = await response.json();
~~~

**Python**

~~~python
import requests

response = requests.get(
    "https://api.fx-port.com/api/v1/bookings/FX-1793546602916",
    params={"product": "esims"},
    headers={"Authorization": "Bearer fxp_test_YOUR_KEY"},
    timeout=30,
)
booking = response.json()["data"]
~~~


## Response examples


**eSIMs**

An eSIM order (a sandbox answer). The summary keys come with their snake_case twins:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "product": "esims",
    "id": "639417b4-b103-4ece-bd5d-850315322278",
    "bookingId": "FX-1793546602916",
    "booking_id": "FX-1793546602916",
    "status": "confirmed",
    "environment": "sandbox",
    "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "agentId": null,
    "agent_id": null,
    "currency": "DZD",
    "totalPrice": 468.84,
    "total_price": 468.84,
    "netPrice": 418.6,
    "b2b_price": 418.6,
    "agencyCommission": 50.24,
    "agency_commission": 50.24,
    "providerName": "Airalo",
    "provider_name": "Airalo",
    "supplierId": "airalo",
    "supplier_id": "airalo",
    "createdAt": "2026-11-01T15:23:22.961228+00:00",
    "created_at": "2026-11-01T15:23:22.961228+00:00",
    "updatedAt": "2026-11-01T15:23:22.961228+00:00",
    "updated_at": "2026-11-01T15:23:22.961228+00:00",
    "orderId": "20261101-106852",
    "order_id": "20261101-106852",
    "type": "esim",
    "packageName": "1 GB - 7 days • France",
    "package_name": "1 GB - 7 days • France",
    "iccid": "8900000540463077957",
    "customerEmail": "traveler@example.com",
    "customer_email": "traveler@example.com",
    "customer_first_name": "John",
    "customer_last_name": "Smith",
    "customer_phone": "+15555550123",
    "record": {
      "id": "639417b4-b103-4ece-bd5d-850315322278",
      "booking_id": "FX-1793546602916",
      "order_id": "20261101-106852",
      "status": "confirmed",
      "currency": "DZD",
      "total_price": 468.84,
      "agency_commission": 50.24,
      "type": "esim",
      "package_name": "1 GB - 7 days • France",
      "iccid": "8900000540463077957",
      "customer_email": "traveler@example.com",
      "customer_first_name": "John",
      "customer_last_name": "Smith",
      "customer_phone": "+15555550123",
      "provider_name": "Airalo",
      "supplier_id": "airalo",
      "created_at": "2026-11-01T15:23:22.961228+00:00",
      "updated_at": "2026-11-01T15:23:22.961228+00:00",
      "environment": "sandbox"
    }
  },
  "meta": {
    "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "agency_id": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d",
    "environment": "sandbox"
  }
}
~~~

**Flights**

A confirmed flight, shortened: the summary keeps only some of its keys (the [history](https://fx-port.com/docs/account/history.md#summary-fields) lists them
all), the snake_case twins are left out, and `record` leaves out its keys without a value:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "product": "flights",
    "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
    "bookingId": "Y7PAEU",
    "status": "confirmed",
    "environment": "sandbox",
    "currency": "DZD",
    "totalPrice": 4930.37,
    "netPrice": 4925.44,
    "agencyCommission": 4.93,
    "providerName": "Amadeus Algeria",
    "supplierId": "amadeus_aqc_dz_1",
    "ticketNumber": "124-2446977397",
    "record": {
      "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
      "booking_id": "Y7PAEU",
      "status": "confirmed",
      "currency": "DZD",
      "total_price": 4930.37,
      "agency_commission": 4.93,
      "departure_airport": "ALG",
      "arrival_airport": "ORN",
      "departure_date": "2026-12-22T19:45:00",
      "arrival_date": "2026-12-22T20:45:00",
      "ticket_number": "124-2446977397",
      "require_acd": false,
      "bucket_link": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed.pdf",
      "bucket_link_no_price": "https://files.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-confirmed-no-price.pdf",
      "paid_at": "2026-11-01T15:26:14.842190+00:00",
      "created_at": "2026-11-01T15:26:14.871035+00:00",
      "updated_at": "2026-11-01T15:26:23.505356+00:00",
      "environment": "sandbox"
    },
    "pricing": [
      {
        "supplier_currency": "DZD",
        "agency_currency": "DZD",
        "base_price": 3160.0,
        "taxes_and_fees": 1765.44,
        "agency_commission": 4.93,
        "final_price": 4930.37,
        "b2b_price": 4925.44,
        "created_at": "2026-11-01T15:26:22.176516+00:00",
        "environment": "sandbox"
      }
    ],
    "travelerPricing": [
      {
        "traveler_id": "1",
        "traveler_type": "ADULT",
        "fare_option": "STANDARD",
        "supplier_currency": "DZD",
        "agency_currency": "DZD",
        "base": 3160.0,
        "total": 4930.37,
        "b2b_price": 4925.44,
        "created_at": "2026-11-01T15:26:15.133066+00:00",
        "environment": "sandbox"
      }
    ],
    "balanceOperations": []
  },
  "meta": { "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d", "environment": "sandbox" }
}
~~~

**Hotels**

A refunded hotel booking, shortened the same way:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "data": {
    "product": "hotels",
    "id": "34a08cf3-dff6-45c6-bdae-882061aa78f5",
    "bookingId": "HMVXX4ZQ3",
    "status": "refunded",
    "environment": "sandbox",
    "currency": "DZD",
    "totalPrice": 1484.97,
    "netPrice": 1482.0,
    "agencyCommission": 2.97,
    "providerName": "ETG - RateHawk",
    "supplierId": "ratehawk",
    "hotelId": "test_hotel_do_not_book",
    "checkinDate": "2026-12-01",
    "checkoutDate": "2026-12-04",
    "record": {
      "id": "34a08cf3-dff6-45c6-bdae-882061aa78f5",
      "booking_id": "HMVXX4ZQ3",
      "status": "refunded",
      "currency": "DZD",
      "total_price": 1484.97,
      "agency_commission": 2.97,
      "hotel_id": "test_hotel_do_not_book",
      "hotel_name": "Test Hotel (Do Not Book) test",
      "checkin_date": "2026-12-01",
      "checkout_date": "2026-12-04",
      "booking_mode": "standard",
      "free_cancellation_deadline_at": null,
      "supplier_id": "ratehawk",
      "refund_amount": 1482.0,
      "refund_currency": "DZD",
      "environment": "sandbox"
    },
    "pricing": [
      {
        "id": "72db7216-177e-4c5b-bde6-74c5ca3c8db2",
        "created_at": "2026-11-01T14:22:05.162117+00:00",
        "agency_currency": "DZD",
        "total_price": 1484.97,
        "agency_commission": 2.97,
        "environment": "sandbox",
        "b2b_price": 1482.0
      }
    ],
    "balanceOperations": []
  },
  "meta": { "agencyId": "0b1c2d3e-4f50-4a6b-8c7d-9e0f1a2b3c4d", "environment": "sandbox" }
}
~~~


A transfer has the summary and the `record` only. The balance operations of a flight or a hotel are empty in the
sandbox.

## Field reference

**Summary.** The keys before `record` are the ones of the [history](https://fx-port.com/docs/account/history.md#summary-fields), with the same
snake_case twins.

**`record`** is the stored booking in snake_case. A key that does not apply to the product is absent.

| Product | Keys of `record` |
|---|---|
| All | `id`, `booking_id`, `status`, `currency`, `total_price`, `agency_commission`, `created_at`, `updated_at`, `environment` |
| Flights | `departure_airport`, `arrival_airport`, `departure_date`, `arrival_date`, `return_departure_date`, `return_arrival_date`, `included_checked_bags`, `ticket_number` (the e-ticket number once issued), `require_acd`, `bucket_link` (the confirmation PDF with prices), `bucket_link_no_price`, `refund_amount`, `refund_currency`, `refund_method`, `paid_at`, `voided_at` |
| Hotels | `hotel_id`, `hotel_name`, `checkin_date`, `checkout_date`, `booking_mode`, `bucket_link`, `bucket_link_no_price`, `refund_amount`, `refund_currency`, `free_cancellation_deadline_at`, `paid_at`, `supplier_id` |
| eSIMs | `order_id`, `type`, `package_name`, `iccid`, `customer_email`, `customer_first_name`, `customer_last_name`, `customer_phone`, `provider_name`, `supplier_id` |
| Transfers | `arrival_date`, `flight_number`, `provider_name`, `bucket_link` (FX-Port's receipt), `voucher_link` (the supplier voucher), `refunded_amount`, `refunded_currency` |

**`pricing[]`** (flights and hotels)

| Field | Description |
|---|---|
| `agency_currency` | Your agency currency |
| `base_price`, `taxes_and_fees` | Flights. The fare before taxes, and the taxes and fees **without** your commission |
| `final_price` | Flights. What the traveler pays: `base_price` + `taxes_and_fees` + `agency_commission` |
| `total_price` | Hotels. What the traveler pays |
| `agency_commission` | Your commission as an amount |
| `b2b_price` | The amount **debited from your agency balance**: the total minus `agency_commission` |
| `supplier_currency` | Flights. The currency the supplier prices in |

**`travelerPricing[]`** (flights; twin `traveler_pricing`): one row per traveler with `traveler_id`, `traveler_type`, `fare_option`,
`base`, `total` and `b2b_price` (the amount debited for this traveler).

**`balanceOperations[]`** (flights and hotels; twin `balance_operations`): the balance operations of the booking with
`id`, `type` (`debit`, `credit` or `refund`), `amount`, `currency`, `description` and `createdAt` (`created_at`). They are the same
operations as the `*_balance_operations` entries of the [balance history](https://fx-port.com/docs/account/balance-history.md).

## Errors

| HTTP | `code` | Cause |
|---|---|---|
| `400` | `invalid_booking_id` | The reference is longer than 100 characters or has characters other than letters, digits and `._:=+~-` |
| `400` | `invalid_product` | `product` is not one of the allowed values |
| `400` | `agency_id_not_allowed` | An API key sent `agency_id` |
| `401` | `authentication_required`, `invalid_api_key` | Missing or invalid API key |
| `404` | `booking_not_found` | No booking of your agency in this environment has this reference, or it belongs to another product than `product` |
| `429` | `rate_limit_exceeded` | Over 6,000 requests per minute for this key |
| `503` | `service_overloaded` | The service is shedding load for a moment (its database pool is full). Nothing was read: retry after the seconds in `Retry-After` (2 to 5) |
