> ## 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.

# Booking and ticketing

> How create order and issue split the purchase, plus retries, price changes and statuses.

Booking is two calls. [Create order](https://developers.ttool.travel/api-reference/book.md) (`POST /orders`) records the order and contacts nobody: no seats are taken and no money moves. [Issue](https://developers.ttool.travel/api-reference/issue.md) (`POST /orders/{orderId}/issue`) is the purchase: it checks your wallet, books the seats with the airline, charges the wallet and returns the tickets, all in one call.

The two are separate so you get an `orderId` and a price to confirm before committing, and so a hold can be requested at creation. They aren't meant to be hours apart: the offer behind an un-issued order expires with the search.

```mermaid theme={null}
flowchart LR
  A[Create order<br/>status Submit] --> B{Issue}
  B --> C[Wallet check]
  C -->|short| X[402, nothing booked]
  C --> D[Reserve seats]
  D -->|price changed| Y[409, nothing charged]
  D --> E[Charge wallet]
  E --> F[Tickets<br/>IssuedSucceeded]
```

To hold the PNR without paying and decide later, see [On-hold bookings](https://developers.ttool.travel/guides/on-hold-bookings.md).

## Retry rules

> **Create order is not idempotent**
>
> Every successful call creates a new order. An un-issued order costs nothing, but check the order status before creating another for the same customer.

> **Issue is idempotent**
>
> Once an order is paid, repeating issue skips the wallet and only reports the ticketing state. Re-call until `status` is terminal.

`cancel-hold` is safe to repeat too: a second call on a released order returns 409 `ORDER_NOT_ON_HOLD`.

## Price changes

The platform never books a changed price silently. The price is re-checked when issue reserves the seats. If it changed, issue returns 409 `FLIGHT_PRICE_CHANGED` with `errorDetails.oldTotalPrice` and `newTotalPrice`, and nothing is charged. Search again, show the customer the new price, and create a fresh order.

Always display `price.total` from the latest response you have, whether that's details, change-options or the order.

## Money safety

| Situation | What happened to the wallet | What to do |
| - | - | - |
| 402 `WALLET_INSUFFICIENT_BALANCE` | Nothing booked, nothing charged | Top up and call issue again |
| 409 `FLIGHT_PRICE_CHANGED` at issue | Nothing charged | Search again, new order |
| 502 `RESERVATION_FAILED` | Nothing charged | Search again |
| 402 `PAYMENT_CONFIRMATION_FAILED` | Seats booked, payment refused | Read the order and contact support. Don't create a new order. |
| 504 `ISSUANCE_TIMEOUT` | Paid, ticketing still running | Call issue again or read the order |

## Issue statuses

HTTP 200 from issue only means the request was processed. Always check `status`.

| `status` | What happened | What to do |
| - | - | - |
| `IssuedSucceeded` | Booked, paid and ticketed | Deliver the tickets |
| `IssueInProgress` | Paid, ticketing still running | Call issue again shortly, or read the [order](https://developers.ttool.travel/api-reference/get-order.md) |
| `OfflineIssueRequired` | Paid; our operations team completes the ticket manually | Tickets follow shortly. Contact support with `orderCode` if they don't arrive. |
| `IssueFailed` | The airline rejected ticketing | Contact support with `orderCode` and the correlation id |
| `PaymentFailed` | Payment didn't complete | Check the wallet, then call issue again |


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