> ## Documentation Index
> Fetch the complete documentation index at: https://developers.ttool.travel/llms.txt
> Use this file to discover all available pages before exploring further.

# On-hold bookings

> Hold a PNR without paying, then issue or release it before the airline's deadline.

Where the airline allows it, you can reserve the PNR **without paying** and decide later. Create the order with `"holdOnly": true`: the order is created, the seats are held at the airline, and nothing is charged. Then either [issue](https://developers.ttool.travel/api-reference/issue.md) it or release it with [cancel-hold](https://developers.ttool.travel/api-reference/cancel-hold.md), in both cases **before `hold.expiresAt`**.

```mermaid theme={null}
stateDiagram-v2
  [*] --> OnHold: create order (holdOnly)
  OnHold --> IssuedSucceeded: issue
  OnHold --> HoldCancelled: cancel-hold
  OnHold --> HoldExpired: deadline passes
```

**1. Check the offer can be held**

Only offers with `supportsOnHold: true` accept `holdOnly`. Otherwise create order returns 400 `HOLD_NOT_SUPPORTED` and no order is created.

**2. Create the order on hold**

Call [create order](https://developers.ttool.travel/api-reference/book.md) with `"holdOnly": true`. The response is the order with `status` `OnHold`, a `pnr` and a `hold` block.

**3. Confirm the deadline**

If `hold.isFinal` is `false` or `hold.expiresAt` is `null`, read the [order](https://developers.ttool.travel/api-reference/get-order.md) again a few minutes later. Amadeus, for example, confirms the real ticketing time limit a few minutes after booking. Once `isFinal` is `true` the value won't move.

**4. Issue or release**

Paying is what tickets it: call [issue](https://developers.ttool.travel/api-reference/issue.md). It skips the reservation step, since the PNR already exists. To give the seats back, call [cancel-hold](https://developers.ttool.travel/api-reference/cancel-hold.md). Nothing was paid, so nothing is refunded.

## The hold block

| Field | Meaning |
| - | - |
| `expiresAt` | Latest moment the order can still be issued. Shortly before it, the platform cancels the PNR at the airline if nothing has happened, so the hold ends in `HoldExpired` with no penalty. |
| `serverTime` | The platform clock when the response was built, on the **same clock** as `expiresAt`. |
| `isFinal` | `false` while the airline may still revise the deadline. |

> [!WARNING]
>
> Compute the remaining time as `expiresAt − serverTime`. Never compare `expiresAt` with your own clock.

## Rules

* A price or itinerary change at hold time returns 409 `FLIGHT_PRICE_CHANGED`, and nothing is held.
* If the airline refuses the hold you get 502 `HOLD_FAILED` and no PNR exists. Retry, create the order without `holdOnly`, or pick another offer.
* Issuing after the hold has ended returns 409 `HOLD_EXPIRED`. The PNR is gone: search again and create a new order.
* `cancel-hold` on an order that isn't `OnHold` returns 409 `ORDER_NOT_ON_HOLD`, so a repeated call is harmless.
* If `cancel-hold` returns `ticketedAtProvider: true`, the airline had already ticketed the PNR (for example your staff issued it in the GDS). Nothing was cancelled and the order is `IssuedSucceeded`.
* About three hours before the deadline, a reminder email goes to the agency account.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.