# Get a flight booking

> Retrieve the full booking through one booking_id field that accepts an internal UUID, a supplier order id or a PNR, with the PDF link.

Endpoint: `POST /api/v1/flights/get_booking`

Source: https://fx-port.com/docs/flights/get-booking

Retrieve a booking by its PNR, its supplier order id or its FX-Port id: the status, the prices, the ticket number, the links of the PDF tickets and the ACD state.

**Permission:** Read. **Rate group:** booking (1,200 requests per minute per key).

## Identifying the booking

The body has one field, `booking_id`, and its value can be any of these:

| `booking_id` value | Description |
|---|---|
| The FX-Port booking `id` (a UUID) | **Recommended**: stable and unambiguous. Returned as `postProcessing.bookingDbId` by [book](https://fx-port.com/docs/flights/book.md) and as `id` here. |
| The supplier order id | `booking.reference` of the booking response: an `ord_...` for Duffel, a base64-like text for Amadeus. Stored in the `amadeus_id` column for **both** suppliers. |
| The PNR | The airline record locator. Not unique across suppliers. |

The lookup tries the PNR first, then the order id, then the UUID, inside your agency and your key's environment. A booking that is not yours, or that belongs to the other environment, is answered `404 booking_not_found`.


> [!WARNING]
> Prefer the UUID or the order id: **PNRs can collide** across suppliers and airlines.


| Field | Type | Required | Description |
|---|---|---|---|
| `booking_id` | string | Yes | One of the three identifiers above. `order_id` and `pnr` are accepted as other names of the same field. |
| `include_api_response` | boolean | No | `true` adds `booking.apiResponse`: the stored answer of the booking call, with the full passenger list. Default `false`. |

Legacy names: `bookingId`, `orderId` and `includeApiResponse` still work and are answered with a `deprecated_field` warning. If `booking_id` is sent together with `order_id` or `pnr` and the values differ, `booking_id` wins and an `ignored_field` warning names the dropped one.

## Request example


**Request**

```bash
curl https://api.fx-port.com/api/v1/flights/get_booking \
  -H "Authorization: Bearer fxp_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "booking_id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7"
}'
```


## Response example

An Amadeus Algeria booking, held and then paid (real response of 2026-11-01, trimmed: the internal columns are left out and the links and ids are fake). `order_id`, `pnr`, `pdf_url` and `pdf_url_no_price` are the snake_case twins of `amadeus_id`, `booking_id`, `bucket_link` and `bucket_link_no_price`; `acd_required` and `requires_acd` say whether ACD passports are expected:

~~~json
{
  "environment": "sandbox",
  "success": true,
  "requestId": "0c2b315c",
  "processingTime": 0.272,
  "booking": {
    "id": "711371c3-00aa-4be2-b790-9f0ca2ccfdb7",
    "booking_id": "Y7PAEU",
    "amadeus_id": "eJzTd9ePNA9wdA0FAAs8AmE",
    "order_id": "eJzTd9ePNA9wdA0FAAs8AmE",
    "pnr": "Y7PAEU",
    "status": "confirmed",
    "created_at": "2026-11-01T15:26:14.871035+00:00",
    "paid_at": "2026-11-01T15:26:21.961664+00:00",
    "updated_at": "2026-11-01T15:26:21.998944+00:00",
    "total_price": 4930.37,
    "agency_commission": 4.93,
    "currency": "DZD",
    "departure_airport": "ALG",
    "arrival_airport": "ORN",
    "departure_date": "2026-12-22T19:45:00",
    "arrival_date": "2026-12-22T20:45:00",
    "return_departure_date": null,
    "return_arrival_date": null,
    "included_checked_bags": "0",
    "ticketing_agreement": "DELAY_TO_CANCEL",
    "supplier_name": "Amadeus Algeria",
    "supplier_id": "amadeus_aqc_dz_1",
    "ticket_number": "124-2446977397",
    "bucket_link": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
    "bucket_link_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
    "pdf_url": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH.pdf",
    "pdf_url_no_price": "https://s3.example.com/flight-bookings/Y7PAEU-JAMES-SMITH-no-price.pdf",
    "email_sent": false,
    "require_acd": false,
    "acd_required": false,
    "requires_acd": false,
    "acd_submitted": false,
    "acd_links": null,
    "acd_amount": null,
    "acd_paid": false,
    "acd_documents_revision": 0,
    "acd_documents": null,
    "has_changed": false,
    "new_flight_details": null,
    "voided_at": null,
    "refund_amount": null,
    "refund_currency": null,
    "refund_method": null,
    "agent_id": null,
    "environment": "sandbox",
    "pricingBreakdown": {
      "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": []
  }
}
~~~

The booking is a **database row**, so its keys are `snake_case`; only `pricingBreakdown`, `travelerPricing`, `balanceOperations` and `apiResponse` are camelCase. The row has more keys than shown (the void history, the details of a changed schedule ...): they are internal, can change and are not part of the contract. Use the keys of the table below. The ids of your agency and of the API key are not returned, and neither are the storage keys of the ACD passports. The booking also carries the twins `order_id` (next to `amadeus_id`), `pnr` (next to `booking_id`), `pdf_url` and `pdf_url_no_price` (next to `bucket_link` and `bucket_link_no_price`).

## Key fields

| Field | Description |
|---|---|
| `id` | The FX-Port UUID of the booking. **Use it as the key** when you store or re-read a booking. |
| `booking_id` | The PNR. Not unique; prefer `id`. |
| `amadeus_id` | The supplier's order id. The name is historical: it also holds the `ord_...` of a Duffel order. This is the `order_id` of `pay_order`, `cancel_order` and `cancel_quote`. |
| `status` | `confirmed`, `awaiting_payment` (a hold), `cancelled`, `partial_refund` (a Duffel order cancelled with a refund), `failed` (the supplier refused the booking). Other values (`voided`, `refunded`, `expired`) can appear for changed orders. |
| `total_price`, `agency_commission`, `currency` | The final price, your commission and the currency of the agency. |
| `pricingBreakdown` | `supplier_currency`, `agency_currency`, `base_price`, `taxes_and_fees`, `agency_commission`, `final_price`, `b2b_price`: **`b2b_price` is what was debited**. The rate and the ACD details are not here. |
| `travelerPricing[n]` | The price of each traveler, as in [search](https://fx-port.com/docs/flights/search.md#travelerpricingn-fields) but in snake_case. |
| `departure_airport`, `arrival_airport`, `departure_date`, `arrival_date`, `return_departure_date`, `return_arrival_date` | The route; the dates are local times without a zone. |
| `ticket_number` | The e-ticket number(s), as one text (several numbers are joined by commas). Can be `null` for a while after an instant Amadeus booking: see [Get the ticket number](https://fx-port.com/docs/flights/get-ticket-number.md). In the Duffel sandbox it is the text `1`. |
| `bucket_link`, `bucket_link_no_price` | The PDF of the ticket with and without the fare. `null` until generated. **These are public links**: see [Get the ticket PDF](https://fx-port.com/docs/flights/get-ticket.md). |
| `ticketing_agreement` | The ticketing-time-limit agreement of an Amadeus booking, for example `DELAY_TO_CANCEL`. |
| `require_acd`, `acd_required`, `requires_acd`, `acd_submitted` | The ACD state. `acd_required` and `requires_acd` are `true` for an ACD booking whose passports FX-Port did not waive (the same value as `acd.acd_required` of `get_acd_documents`); `require_acd` is the stored flag. `acd_submitted` is a boolean, never `null`: `false` until every traveler has a passport. See [ACD passport uploads](https://fx-port.com/docs/flights/acd-documents.md). |
| `acd_documents`, `acd_links`, `acd_amount`, `acd_paid`, `acd_documents_revision` | The passports of an ACD booking. `acd_documents` is `null` until a passport is uploaded through the API, then it has one entry for each traveler: `traveler_id`, `traveler_name`, `traveler_type`, `link`, `filename`, `content_type`, `size` (bytes), `format`, `uploaded_at`, `replaced_count`, `previous_links[]` (`link`, `uploaded_at`, `replaced_at`: the files that were replaced) and `source`. `acd_links` lists the addresses of every passport file, also the ones FX-Port staff added from the dashboard. **These links are public**: see [Security: file links are public](https://fx-port.com/docs/flights/acd-documents.md#security-file-links-are-public). FX-Port's own storage keys are never returned. For the state of each traveler use [`get_acd_documents`](https://fx-port.com/docs/flights/acd-documents.md). `acd_amount` is the ACD amount in euros, saved when the set became complete (informational); `acd_paid` says whether FX-Port's team marked it as paid; `acd_documents_revision` goes up with each upload that stored a file. |
| `has_changed`, `new_flight_details` | Set when the airline changed the schedule after ticketing. |
| `refund_amount`, `refund_currency`, `refund_method` | The refund of a cancelled Duffel order (`partial_refund`, method `balance`). |
| `voided_at` | When the ticket was voided. |
| `email_sent` | Whether the confirmation e-mail went out. |
| `environment` | `sandbox` or `live`. |
| `balanceOperations` | The ledger entries of this booking (debits, refunds), each with only `id`, `flight_booking_id`, `operation`, `amount`, `currency`, `description`, `created_at`, `b2b_price` and `final_price`. Empty in the sandbox. |

### An ACD booking with a passport

The ACD keys of the booking of an ACD fare whose passport was uploaded and then replaced once (real response of 2026-11-01, trimmed to these keys; the name and the links are fake). Every other key is the one of the first example.

~~~json
{
  "booking": {
    "booking_id": "Y7HWMD",
    "require_acd": true,
    "acd_required": true,
    "requires_acd": true,
    "acd_submitted": true,
    "acd_links": ["https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png"],
    "acd_amount": 235.0,
    "acd_paid": false,
    "acd_documents_revision": 2,
    "acd_documents": [
      {
        "traveler_id": "1",
        "traveler_name": "JANE DOE",
        "traveler_type": "ADULT",
        "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142432_087a6dc8.png",
        "filename": "rescan.png",
        "content_type": "image/png",
        "size": 69,
        "format": "png",
        "uploaded_at": "2026-11-01T14:24:33.057044+00:00",
        "replaced_count": 1,
        "previous_links": [
          {
            "link": "https://s3.example.com/fx-port/acd_passports/Y7HWMD/1/20261101142430_9b354245.pdf",
            "uploaded_at": "2026-11-01T14:24:31.256905+00:00",
            "replaced_at": "2026-11-01T14:24:33.057044+00:00"
          }
        ],
        "source": "api"
      }
    ]
  }
}
~~~

### The PDF

The PDF is generated after the booking, typically in 10 to 30 seconds:


**Step 1**

Call `get_booking` and look at `booking.bucket_link`.

**Step 2**

If it is `null`, the PDF is still being made: wait 5 to 10 seconds and read again. A **failed** booking never gets one, so stop after about two minutes.

**Step 3**

When it has a value it is the download address of the PDF. A **held** booking gets a **hold voucher**, not a ticket; after the payment the link points to the ticket.


`POST /flights/get_ticket` returns the same links with a `ticket_ready` flag.

## Errors

| Status | Code | When | What to do |
|---|---|---|---|
| `400` | `bad_request` | No `booking_id` (`booking_id is required`). | Send it. |
| `400` | `invalid_booking_id` | `booking_id` is not a valid token: letters, digits and `- _ . : = ~`, up to 128 characters. | Send the id as you received it. |
| `400` | `invalid_json`, `invalid_body` | The body is not a JSON object. | Send a JSON object. |
| `404` | `booking_not_found` | No booking with that id in your agency and environment (a booking of another agency is answered the same way). | Check the id and the key. |

## Related

- [Book a flight](https://fx-port.com/docs/flights/book.md), [Get the ticket PDF](https://fx-port.com/docs/flights/get-ticket.md) and [Get the ticket number](https://fx-port.com/docs/flights/get-ticket-number.md).
- [Booking detail](https://fx-port.com/docs/account/booking-detail.md): the same booking across products, by GET.
