---
title: "Book a flight offer on the connected Revolut method"
method: POST
path: "/flights/book"
tags: ["flights"]
---

# Book a flight offer on the connected Revolut method

`POST /flights/book`

## Request

- Base URL: `https://letsfg.co/developers/api/v1`
- URL: `https://letsfg.co/developers/api/v1/flights/book`
- Auth: API key in header `X-API-Key`

Book one offer from one of your own searches.

The connected Revolut method (POST /agents/connect-payment) is **held, not charged**, for the price shown. A LetsFG booking agent buys the ticket from the seller and the hold is captured **only against a real airline PNR**; a failed booking releases it and nothing is taken.

Answers 202 within seconds with a booking_id — the booking itself takes 4–11 minutes. Poll GET /flights/bookings/{booking_id} until `terminal` is true. A completed booking also resets your free search allowance.

Passengers use the same shape as the MCP book_flight tool, field for field. Only given_name is required by the schema: the server decides what a given airline checkout needs and answers 400 `missing_fields` naming exactly those, before anything is charged. 1–9 passengers.

Always send an idempotency_key — retrying with the same key returns the existing booking instead of opening a second hold.

There is no booking fee and no transaction fee on top: the amount the search returned is the amount charged.

## Request body

- DeveloperBookRequest
  - `search_id` string, required: search_id from the search response. An offer is bookable only inside its own search.
  - `offer_id` string, required: id of one offer from that search
  - `passengers` DeveloperPassenger[], required
    - `given_name` string, required: First/given name exactly as on the travel document
    - `family_name` string: Surname exactly as on the travel document
    - `no_family_name` boolean: True when the traveller genuinely has no surname
    - `born_on` string: YYYY-MM-DD
    - `gender` string: 'm' or 'f' — the airline form's title is derived from it
    - `email` string, email: The lead passenger's email receives the airline confirmation
    - `phone_number` string
    - `phone_country` string: ISO-3166 alpha-2, e.g. PL
    - `nationality` string: ISO-3166 alpha-2
    - `address_line1` string
    - `address_line2` string
    - `address_city` string
    - `address_state` string
    - `address_postal` string
    - `address_country` string: ISO-3166 alpha-2 country of residence
    - `passport_number` string: Required for every traveller - some sellers will not issue a ticket without it. A missing one is answered as missing_fields.
    - `passport_country` string: ISO-3166 alpha-2 issuing country. Required.
    - `passport_expiry` string: YYYY-MM-DD. Required; must be valid through the last flight.
    - `passport_issue` string: YYYY-MM-DD, the date printed as 'Date issued'. Required.
    - `passenger_type` 'adult' | 'child' | 'infant'
  - `contact_email` string, email: Where the confirmation goes. Defaults to the lead passenger's email.
  - `idempotency_key` string: Retrying with the same key returns the same booking instead of opening a second hold.
  - `want_seat` boolean: Pause at the airline's seat map and ask.

## Response `202`

Hold placed and the booking agent dispatched.

- BookAccepted
  - `ok` boolean
  - `booking_id` string
  - `state` string
  - `dispatched` boolean
  - `held` object: What is HELD on the connected method — not taken.
  - `charged` number: Always 0 here. Capture happens only against a real PNR.
  - `offer` object
  - `poll_url` string, uri
  - `message` string

## Other responses

- `400`: missing_fields (listed), party_too_large, currency_unsupported, or offer_not_bookable. Nothing charged.
- `402`: payment_method_required with pay_url (the trip paid on its own page, which starts the booking), or payment_declined with a decline_reason (and, when the payment can go on as 3-D Secure, pay_url + retry_booking_id). Nothing charged by the call.
- `404`: search_not_found or offer_not_found — a search stays bookable for 6 hours.
- `409`: package_blocked — flights and stays are sold separately, never together: this customer already has the other type of service for an overlapping trip. `code` is `one_service_per_visit`, `retry` is false, and `message` is the sentence for the traveller. Do not retry.

---

[API](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api.md) · [All operations](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/letsfg/apis/letsfg-public-developer-api/revisions/516ab6ba1d2e?raw)
