# The booking flow

A complete booking moves through a fixed lifecycle:

```
content  →  search  →  prebook  →  book  →  (poll bookings/{id})  →  cancel
```

* **content** — your locally cached catalogue (see [Property content](/developer/property-content.md)).
* **search** — real-time availability and pricing for dates and occupancy.
* **prebook** — revalidate a chosen rate and pin its price + cancellation policy behind a short-lived token.
* **book** — finalize the reservation idempotently.
* **poll** — read the booking back to resolve any non-terminal status.
* **cancel** — quote the penalty, then cancel.

All booking endpoints require the `distribution:booking` scope, except `search` which requires `distribution:read`.

## Step 1 — Search

```
POST /api/v1/search
```

Returns ranked, bundled room allocations for one or more properties over a stay window. The request is multi-property by default: `property_ids` holds 1–100 entries and the response carries one `results[]` entry per property in request order.

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/v1/search \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Content-Type: application/json' \
  -d '{
    "property_ids": ["7gQ2kPa9"],
    "checkin": "2026-06-10",
    "checkout": "2026-06-13",
    "occupancy": [{ "adults": 2, "child_ages": [7] }],
    "currency": "TRY"
  }'
```

`occupancy` is a per-room list (slot 0 is the first room). A single slot triggers single-room matching with an automatic multi-room-split fallback; multiple slots are matched strictly. Currency, occupancy, and the allocation model are covered in depth in [Currencies, occupancy & allocations](/developer/currencies-occupancy-allocations.md) — here we focus on the lifecycle.

```json
{
  "currency_code": "TRY",
  "results": [
    {
      "property_id": "7gQ2kPa9",
      "total_available": 1,
      "total_blocked": 0,
      "allocations": [
        {
          "allocation_id": "alc_5f3a9c1d8e7b4a2f6c0d9e8b7a6f5c4d",
          "rooms": 1,
          "strategy": "single-room",
          "grand_total": "9000.00",
          "currency": "TRY",
          "slots": [
            {
              "rate_key": "rp_bar:r3Tz9Lm0:single",
              "room_public_id": "r3Tz9Lm0",
              "rate_plan_public_id": "rp_bar",
              "meal_plan_code": "BB",
              "stay_total": "9000.00",
              "status": "AVAILABLE"
            }
          ]
        }
      ]
    }
  ]
}
```

Carry the chosen `allocation_id` (and the slot `rate_key`) into prebook. Per-property failures surface inline on `results[i].error_code` (`PROPERTY_NOT_FOUND`, `UNSUPPORTED_FX_PAIR`, `INTERNAL_ERROR`) and do **not** fail the whole request.

## Step 2 — Prebook

```
POST /api/v1/prebook
```

Revalidates the offered rate against current availability and mints a short-lived token that pins price, cancellation policy, and an inventory hold for the subsequent book call. The `Idempotency-Key` header is **mandatory** — see [Idempotency](/developer/idempotency.md).

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/v1/prebook \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Idempotency-Key: 9f1c0b2a-4d6e-4f8a-9c1d-2e3f4a5b6c7d' \
  -H 'Content-Type: application/json' \
  -d '{
    "property_public_id": "7gQ2kPa9",
    "rate_key": "rp_bar:r3Tz9Lm0:single",
    "checkin": "2026-06-10",
    "nights": 3,
    "adults": 2,
    "child_ages": [7],
    "currency_code": "TRY"
  }'
```

```json
{
  "match_status": "MATCHED",
  "prebook_token": "<PREBOOK_TOKEN>",
  "prebook_expires_at": "2026-06-02T12:30:00Z",
  "offer": { "grand_total": "9000.00", "currency": "TRY" },
  "cancellation_policy": {
    "currency": "TRY",
    "finalized": true,
    "tiers": [{ "from": "2026-06-08T00:00:00Z", "fee": "4500.00", "currency": "TRY" }]
  },
  "warnings": [],
  "trace_id": "0f2c9d6e-7b1a-4c3e-9a2f-1d6e7b1a4c3e"
}
```

### `match_status`

`match_status` reports how the freshly revalidated offer compares to the one you searched. Always inspect it before booking:

| `match_status`  | Meaning                                        | What to do                                                     |
| --------------- | ---------------------------------------------- | -------------------------------------------------------------- |
| `MATCHED`       | Exact agreement; price and board unchanged.    | Safe to proceed to `/book` with the returned token.            |
| `PRICE_CHANGED` | The stay total moved (either direction).       | Re-confirm the new price with the end traveller, then `/book`. |
| `BOARD_CHANGED` | The meal plan differs from the searched offer. | Re-confirm the board with the end traveller, then `/book`.     |
| `SOLD_OUT`      | The offer is no longer available.              | The flow ends; `/book` would reject anyway.                    |

The `cancellation_policy` block is the **finalized** policy (`finalized: true`) — the exact terms that will be snapshotted onto the booking. The token (`prebook_token`) is valid for 30 minutes (`prebook_expires_at`); booking after it expires returns a `409` with code `PREBOOK_EXPIRED`.

## Step 3 — Book

```
POST /api/v1/book
```

Consumes the prebook token, writes the booking, decrements inventory, and snapshots the cancellation policy. The `Idempotency-Key` header is **mandatory** — full semantics in [Idempotency](/developer/idempotency.md).

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/v1/book \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Idempotency-Key: 3b2a1c0d-5e6f-4a8b-9c0d-1e2f3a4b5c6d' \
  -H 'Content-Type: application/json' \
  -d '{
    "prebook_token": "<PREBOOK_TOKEN>",
    "contact": {
      "first_name": "Ada",
      "last_name": "Lovelace",
      "email": "ada@example.io"
    },
    "guests": [[
      { "kind": "ADULT", "first_name": "Ada", "last_name": "Lovelace", "is_lead": true }
    ]],
    "agency_reference": "your-ref-12345"
  }'
```

`guests` is a list of per-room guest lists, aligned with the prebooked allocation's slots. On success you receive `201 Created`:

```json
{
  "booking": {
    "public_id": "bk_8H2nQ4pZ",
    "booking_number": 100482,
    "status": "CONFIRMED",
    "stay_total": "9000.00",
    "currency": "TRY",
    "checkin_date": "2026-06-10",
    "checkout_date": "2026-06-13",
    "nights": 3,
    "cancellation_policy_snapshot": { "finalized": true },
    "rooms": [{ "room_public_id": "r3Tz9Lm0", "rate_plan_public_id": "rp_bar" }],
    "guests": [[{ "kind": "ADULT", "first_name": "Ada", "last_name": "Lovelace" }]]
  },
  "trace_id": "0f2c9d6e-7b1a-4c3e-9a2f-1d6e7b1a4c3e"
}
```

### Status codes on book

| Code  | Meaning                                                     |
| ----- | ----------------------------------------------------------- |
| `201` | Booking finalized synchronously.                            |
| `200` | Idempotent replay of an earlier finalized booking.          |
| `202` | Finalize is in progress; the response carries a `poll_url`. |

When you receive `202` (or whenever `booking.status` is non-terminal such as `ON_REQUEST` or `PENDING`), resolve the outcome by polling step 4.

## Step 4 — Poll the booking

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

Reads the persisted booking by its `public_id`. Use it to confirm a booking reached its terminal state after `/book` returns, and to resolve any `OnRequest` / `Pending` booking — there is no partner-facing confirm/decline action; the booking transitions to its terminal status on its own and you observe it by polling.

```bash
curl -s \
  https://ari.console.adrasis.com/api/v1/bookings/bk_8H2nQ4pZ \
  -H 'Authorization: Bearer eyJ...'
```

```json
{
  "public_id": "bk_8H2nQ4pZ",
  "booking_number": 100482,
  "status": "CONFIRMED",
  "stay_total": "9000.00",
  "currency": "TRY",
  "checkin_date": "2026-06-10",
  "checkout_date": "2026-06-13",
  "nights": 3
}
```

Poll until `status` reaches a terminal value (for example `CONFIRMED` or `CANCELLED`) with a backoff between calls.

## Step 5 — Cancel (two-phase)

Cancellation is two-phase: quote the penalty first, then commit the cancel.

### 5a. Cancellation quote

```
POST /api/v1/bookings/{public_id}/cancellation-quote
```

Computes the penalty a cancellation would realise, with **no side effects**. The fee is derived from the frozen cancellation policy snapshot using the same rule the cancel path applies.

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/v1/bookings/bk_8H2nQ4pZ/cancellation-quote \
  -H 'Authorization: Bearer eyJ...'
```

```json
{
  "fee": "450.00",
  "currency": "TRY",
  "cancellable": true,
  "cancellation_deadline": "2026-06-08T00:00:00+00:00",
  "trace_id": "0f2c9d6e-7b1a-4c3e-9a2f-1d6e7b1a4c3e"
}
```

`cancellable` reports whether the booking is in a status that can be cancelled. `fee` is `"0"` when the booking is still inside its free-cancel window; `cancellation_deadline` (when present) is the instant up to which no penalty applies.

### 5b. Cancel

```
POST /api/v1/bookings/{public_id}/cancel
```

Commits the cancellation through the two-phase transition, snapshots the realised penalty, and enqueues the cancellation notification. The body is optional — an empty body is a valid cancel.

```bash
curl -s -X POST \
  https://ari.console.adrasis.com/api/v1/bookings/bk_8H2nQ4pZ/cancel \
  -H 'Authorization: Bearer eyJ...' \
  -H 'Content-Type: application/json' \
  -d '{
    "reason": "guest requested cancellation",
    "notify_guest": true,
    "notify_channel": true
  }'
```

```json
{
  "booking": {
    "public_id": "bk_8H2nQ4pZ",
    "status": "CANCELLED",
    "stay_total": "9000.00",
    "currency": "TRY",
    "checkin_date": "2026-06-10",
    "checkout_date": "2026-06-13",
    "nights": 3
  },
  "trace_id": "0f2c9d6e-7b1a-4c3e-9a2f-1d6e7b1a4c3e"
}
```

Cancel is idempotent: a booking that is already `CANCELLED` returns `200` with the existing booking envelope and performs no further writes. A booking in a non-cancellable status, or past its cancellation deadline, returns `409` with code `BOOKING_NOT_CANCELLABLE`.

## Next steps

* [Idempotency](/developer/idempotency.md) — the mandatory key on prebook and book.
* [Currencies, occupancy & allocations](/developer/currencies-occupancy-allocations.md) — the search request and allocation model in depth.
* [Errors](/developer/errors.md) — the complete status-code reference.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://adrasis.gitbook.io/developer/booking-flow.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
