# Quick book API (restricted)

> A restricted API for approved integration clients to sell Panion tours at a desk and book them after payment.

Source: https://www.panion.travel/docs/api/quickbook

> **Restricted access:** The Quick book API is not public. Only Panion-approved integration partners can use it, with a key Panion issues. Requests without such a key are refused. To ask for access, contact Panion at [hello@panion.travel](mailto:hello@panion.travel).

The Quick book API lets an approved integration client sell Panion tours at a desk or counter: list the tours its terminal can sell, quote a sale, take payment with the payment provider, then book. Panion derives the operators and tours from the terminal; the client never names a partner or merchant.

Main path: GET /tours, GET /tours/{activityId}/slots, POST /quote (holds nothing), take payment following the cart line's `payment` instructions with the sale `reference` as the payment's reference, then POST /book. Holding seats with POST /reserve is optional, for scarce departures.

Money is integer minor units with an ISO 4217 currency. Departure times are local wall-clock times with an IANA time zone.

Base URL: https://www.panion.travel/api/quickbook/v1. Machine-readable: https://www.panion.travel/api/quickbook/v1/openapi.json (OpenAPI 3.1).

Endpoints:

- GET /api/quickbook/v1/tours: List the tours this terminal can sell on a day.
- GET /api/quickbook/v1/tours/{activityId}/slots: Start times and guest prices for a tour on a day.
- POST /api/quickbook/v1/quote: Quote a sale and get the cart line.
- POST /api/quickbook/v1/reserve: Optional: hold the seats of a quoted sale while the guest pays.
- DELETE /api/quickbook/v1/reserve/{holdRef}: Optional: release a hold.
- POST /api/quickbook/v1/book: Book a paid sale.
- GET /api/quickbook/v1/sales/{reference}: Status of a sale.
- GET /api/quickbook/v1/sales/{reference}/ticket: Reprint the ticket of a confirmed sale.

## Authentication

Every call sends the key as `Authorization: Bearer pqb_live_...` (test keys: `pqb_test_...`) and the id of the payment terminal in the `Quickbook-Terminal` header. Panion issues the key and binds it to at least one terminal; a call naming a terminal the key is not bound to is refused. Never put the key in a URL: a key in the query string is refused.

## Errors

Errors answer with a JSON body `{"error": {"code", "clerkMessage", "reference"?}}`, an `X-Request-Id` header and `Cache-Control: no-store`. Branch on `code`; show `clerkMessage` to the person at the desk.

- 409 `sold_out`: Not enough seats left on this departure.
- 409 `price_changed`: The live price differs from the quote, and the quote is past `validUntil`. From `quote`: a guest type the tour does not sell (read the slots again).
- 503 `provider_unavailable`: The tour operator's booking system did not answer. Try again in a moment.
- 410 `hold_expired`: The seat hold ran out.
- 422 `hold_not_supported`: This tour's booking system cannot hold seats. Carry on without `reserve`: take payment and call `book`.
- 403 `terminal_not_registered`: The terminal in the `Quickbook-Terminal` header is not registered, not active or not bound to this key. A missing or malformed header answers 400 with this code.
- 403 `tour_not_sellable_here`: This terminal cannot charge for this tour's operator, so it cannot sell it.
- 422 `phone_required`: This tour needs the guest's phone number.
- 422 `phone_rejected`: The tour operator did not accept the phone number.
- 422 `payment_not_found`: Panion cannot find this payment at the payment provider. Nothing was booked.
- 409 `payment_mismatch`: The payment does not match the sale (amount, currency, merchant, reference or terminal). Nothing was booked, nothing is refunded automatically, and Panion is alerted.
- 409 `duplicate_payment`: The sale is already paid with a different payment. The second payment is refunded.
- 409 `provider_refused`: The tour operator refused the booking.
- 401 `unauthorized`: The Quick book key is missing, not valid, expired or revoked, or was sent in the URL. Answers with `WWW-Authenticate: Bearer realm="panion-quickbook"`.
- 429 `rate_limited`: Too many requests from this terminal: 60 writes, 60 books and 300 reads per minute. `Retry-After` says when to try again.
- 400 `invalid_request`: The body or query does not validate (a missing field, a bad date or number).
- 404 `not_found`: No such sale or hold for this terminal: the reference is unknown or belongs to another terminal.

When `book` fails after the guest has paid, it answers 200 with `status: "failed"`, the `reason` (one of the codes above), `moneyReturned` and a `clerkMessage`: the payment is refunded automatically.

Sale statuses (GET /sales/{reference}): `quoted`, `held`, `expired`, `booking`, `confirmed`, `failed`.

## Limits

Rate limits per terminal per minute: 60 writes (quote, reserve, release), 60 books (book, its own budget so quotes never crowd out a paid sale) and 300 reads (tours, slots, sales). Past a limit: 429 `rate_limited` with `Retry-After`.

## Endpoints

- [Quick book: tours](https://www.panion.travel/docs/api/quickbook/tours): The tours an integration client can sell on a day, with start times and prices.
- [Quick book: sales](https://www.panion.travel/docs/api/quickbook/sales): Quote a sale, book it after payment, read its status and reprint the ticket.
- [Quick book: holds (optional)](https://www.panion.travel/docs/api/quickbook/holds): Optional seat holds for scarce departures while the guest pays, and releasing them.

> The OpenAPI 3.1 document is at [/api/quickbook/v1/openapi.json](https://www.panion.travel/api/quickbook/v1/openapi.json).
