# Booking flows

> Step-by-step flight booking flows: instant issue, hold then pay, and how cancelling a held PNR works.

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

Every flight booking follows one of two flows. Both start with a search and a mandatory pricing step, and a booking can be cancelled in different ways depending on its state and its supplier.

## Instant issue (most common)

The tickets are issued when you book.


Instant issue, end to end. Actors: Your server, FX-Port API, Supplier.

1. Your server to FX-Port API: POST /get_flights
2. FX-Port API to Supplier: Search every enabled supplier
3. Supplier to FX-Port API: Fares
4. FX-Port API to Your server: Offers, one search_id per supplier
5. Your server to FX-Port API: POST /flights/price_flight
6. FX-Port API to Your server: Confirmed fare, offer cached
7. Your server to FX-Port API: POST /flights/book_flight (issue)
8. FX-Port API to Supplier: Book and issue the tickets
9. FX-Port API to Your server: Booking, PNR and ticket numbers
10. Your server to FX-Port API: POST /flights/get_booking
11. FX-Port API to Your server: Booking with the PDF link

**Step 1**

**Search for flights**

`POST /api/v1/get_flights` returns offers from one or every supplier. Each supplier entry has a `searchId`: keep it for the next steps. See [Search flights](https://fx-port.com/docs/flights/search.md).

**Step 2**

**Price the offer** *(required)*

`POST /api/v1/flights/price_flight` confirms the current fare and saves the priced offer. This step is **mandatory**: booking an offer that was not priced is refused with `404 offer_not_priced`. See [Price a flight offer](https://fx-port.com/docs/flights/price.md).

**Step 3**

**Book and issue the tickets**

`POST /api/v1/flights/book_flight` with `"booking_type": "issue"`. The tickets are issued at once. The response has `booking.reference` (the supplier's order id), `booking.pnr` and the ticket documents. See [Book a flight](https://fx-port.com/docs/flights/book.md).

**Step 4**

**Read the booking and the PDF**

`POST /api/v1/flights/get_booking` returns the whole booking. The PDF link (`booking.bucket_link`) appears a few seconds after the booking. See [Get a flight booking](https://fx-port.com/docs/flights/get-booking.md) and [Get the ticket PDF](https://fx-port.com/docs/flights/get-ticket.md).


~~~text
GET_FLIGHTS -> PRICE_FLIGHT -> BOOK_FLIGHT (issue) -> GET_BOOKING
~~~

Two rules that surprise people:

- **A booking is idempotent per offer.** Booking an offer twice answers `409 duplicate_booking` with the PNR of the first booking, and a second request while the first is running answers `409 booking_in_progress`. A booking that failed releases the offer, so you can try again.
- **Duffel books one offer per search.** After a Duffel booking, booking another offer of the **same** search fails with `409 offer_request_already_booked`: search again first.

---

## Hold, then pay (suppliers that support holds)

Reserve the seats, pay later. Use it only when the **priced** offer says `supportHold: true` (today Amadeus Algeria). Never infer hold support from a supplier name: it depends on the supplier, the airline, the fare and the offer.


Hold, then pay. Actors: Your server, FX-Port API, Supplier.

1. Your server to FX-Port API: POST /get_flights, then /flights/price_flight
2. Your server to FX-Port API: POST /flights/book_flight (hold)
3. FX-Port API to Supplier: Create the reservation, no ticket
4. FX-Port API to Your server: Held booking, awaiting_payment
5. Your server to FX-Port API: POST /flights/get_order_quote (optional)
6. FX-Port API to Your server: Current price and deadline
7. Your server to FX-Port API: POST /flights/pay_order
8. FX-Port API to Supplier: Issue the tickets, debit the balance
9. FX-Port API to Your server: Ticketed booking

**Step 1**

**Search and price**: as for an instant issue.

**Step 2**

**Create the hold**

`POST /api/v1/flights/book_flight` with `"booking_type": "hold"`. The reservation is created without tickets and without a charge. The booking answer says `booking.status: "pending"`; every later answer (quote, `get_booking`) says `awaiting_payment`. A `hold` on an offer that cannot be held is refused with `400 hold_not_supported`.

**Step 3**

**Preview the price** *(optional)*

`POST /api/v1/flights/get_order_quote` returns the current price and whether the hold can still be paid. See [Quote a held booking](https://fx-port.com/docs/flights/get-order-quote.md).

**Step 4**

**Pay and issue**

`POST /api/v1/flights/pay_order` debits the agency balance and issues the tickets. Send the `order_id`. Do **not** send `currency`: it is refused. See [Pay a held booking](https://fx-port.com/docs/flights/pay-order.md).

> [!CAUTION]
> **A hold is not guaranteed until the payment succeeds.** `pay_order` fails when the airline has already released the reservation, when the deadline has passed, or when the booking is cancelled. Airlines can expire a hold earlier than the time that was displayed. The answer of `pay_order` is the only truth: handle a failure without charging the traveler, and search again for a new booking.


~~~text
GET_FLIGHTS -> PRICE_FLIGHT -> BOOK_FLIGHT (hold) -> [GET_ORDER_QUOTE] -> PAY_ORDER -> GET_BOOKING
~~~

---

## Cancelling

What you can cancel by yourself depends on the state and on the supplier:

| Booking | How | Money |
|---|---|---|
| A **held** (unpaid) reservation, Amadeus Algeria | [`cancel_order`](https://fx-port.com/docs/flights/cancel-order.md): deletes the reservation and releases the PNR | Nothing was paid: nothing is refunded |
| An **issued** Duffel order | [`cancel_quote`](https://fx-port.com/docs/flights/cancel-quote.md) asks what the airline would refund, then [`confirm_cancel`](https://fx-port.com/docs/flights/confirm-cancel.md) cancels for good | The refund is credited to your FX-Port balance. Only for orders the airline lets you cancel (`order_not_cancellable` otherwise) |
| An **issued** Amadeus Algeria ticket: void, exchange, re-issue, refund | Manual: see [Post-booking operations](https://fx-port.com/docs/flights/post-booking.md) | By the FX-Port team |

### Why cancel a hold you abandon

If you create a hold and then abandon it, the airline can **refuse a new booking for the same passenger** on the same route while the old reservation is pending. Cancel the old hold with `cancel_order` to release the PNR before you book that passenger again.

~~~text
BOOK_FLIGHT (hold) -> (you change your mind) -> CANCEL_ORDER -> PNR released -> safe to book again
~~~

## After the booking

- `get_booking` is the source of truth for the status, the prices and the PDF links.
- `get_ticket` returns the PDF links, and `get_ticket_number` the ticket number when the airline has issued it: see [Get the ticket number](https://fx-port.com/docs/flights/get-ticket-number.md).
- A booking that needs passports (`requiresACD`) is completed with [ACD passport uploads](https://fx-port.com/docs/flights/acd-documents.md).
- There are **no webhooks**: poll `get_booking`, not more often than every few seconds.
