The Offers Flow
The Distribusion API identifies a bookable fare by a persistent offer_id. A search returns an
offer id for each fare it finds; that id is then the only trip context the following calls need.
Vacancy is checked by offer id, and a reservation is created from it — neither call re-sends station
codes, times, carrier codes or fare classes.
This applies to the first three steps of the flow: /connections/find, /connections/vacancy and /reservations/create. Confirmation, retrieval, cancellation and amendment are unaffected and continue to work from the reservation_id and booking_id.
Offer ids also carry the structure that segment-level fares, ancillaries and seat maps depend on, so
anything beyond a single undifferentiated fare per connection is expressed through them.
Integrations built before offers were introduced continue to work unchanged. The legacy parameter-based calls are still accepted — see Migrating from the legacy
flow at the foot of this page.
What an offer is
An offer is one bookable commercial proposition: a specific fare, for specific passengers, over specific segments, at a stated price. Where a connection describes the journey — times, stations, vehicles — an offer describes what it costs and on what terms.
Each offer carries an offer_id, in a format such as 7a9bd0be-e5c8-4995-a259-faf096e78493|0. The id is issued by /connections/find and is the handle for that fare through the rest of the flow. It is distinct from the resource id on the object that contains it.
An offer id is not valid indefinitely. Offers are typically valid for 30 minutes from the search that produced them, and each offer carries an offer_expires_at timestamp which is authoritative — some are issued with a shorter or longer window. Once it has passed, the offer cannot be used for /connections/vacancy or /reservations/create and must be replaced by a fresh search. This is separate from the hold on a reservation, which has its own expires_at and defaults to 10 minutes.
An offer states its own scope rather than leaving it to be inferred. passenger_ids and segment_ids name exactly who and what it covers, and passenger_coverage_type and segment_coverage_type say whether the price applies once across that set or once per member of it.booking_type says whether the offer stands on its own, needs a counterpart, or is a component of another offer. Because scope is stated, a fare that differs by segment, or an ancillary that applies to one passenger on one leg, is expressible without a separate mechanism.
Working with offer ids
/connections/find and /connections/vacancy accept POST with a JSON body. GET with query parameters is still accepted, but the body form is what the offers flow is documented against and is the recommended method for new integrations.
Locations are given as a single departure_location and arrival_location object, each stating a type and a value, rather than as separate departure_station and departure_city parameters. Trip context is grouped into a search_bounds array, which is what makes multi-bound journeys expressible.
Passengers are assigned ids at search time — pax-40-1 and so on — and those ids are referenced at every following step. This is what ties a fare to a named traveller rather than to a count, and it is what makes a per-passenger price legible when an offer covers several people.
From there the flow is:
- Search.
/connections/findreturns offers, each with anoffer_idand its ownfare_conditions. - Check price and vacancy.
/connections/vacancytakes the selectedoffer_id, or several for a return, together with thepassenger_idseach one applies to. No trip parameters are re-sent. - Reserve.
/reservations/createtakes the verifiedoffer_idin itsoffersarray, and binds traveller data —first_name,last_name,birthdate— to the passenger ids established at search.
For a return journey, the recommended approach is two /connections/find calls: pass the chosen outbound offer_id in the offers array of the inbound search. Carriers that price a return against its outbound — SNCF and Eurostar among them — only surface the discounted inbound fare when the outbound offer is supplied.
After the reservation
Offers end at reservation creation. Everything past that point is keyed on identifiers the offers flow does not touch.
- Confirmation and status.
/reservations/confirmand/reservations/{reservation_id}are unchanged and work from thereservation_id. - Post-booking. Retrieval, ticketing and cancellation are unchanged and work from the
booking_id. See Identifiers and their lifetime for which of these survive an amendment. - Ancillaries and seat maps. Both are expressed as offers, with the same
offer_idhandle and the same coverage fields as a fare. See Ancillaries and Seat Map.
Migrating from the legacy flow
Integrations that predate offers send trip parameters on every call, and are still accepted. The tables below map each legacy call onto its offers equivalent, for anyone moving an existing integration across.
Step 1: Searching for Trips /connections/find
Method & Payload |
|
|
Locations | Separate arrays or fields like | Unified |
Trip Context | Flat departure dates and times in the query string. | Grouped within a |
Passengers | Flat array (e.g., | Structured passengers array in the JSON body that assigns unique IDs (e.g., |
Return Trips | Passing a | A two-step /connections/find process is recommended. Pass the selected outbound |
Output | Connection details. | Returns a unique |
Step 2: Checking Price & Vacancy /connections/vacancy
Method & Payload |
|
|
Trip Details | Required reconstructing the trip by re-sending | Completely removed. You now only pass the |
Passenger Mapping | Sent flat passenger counts and types again (e.g., | Mapped using the |
Step 3: Creating a Reservation /reservations/create
Method & Payload |
|
|
Trip Details | The payload root required multiple explicit trip details ( | Explicit trip details are gone. You only pass the verified |
Passenger Binding | Passed a completely new passengers array defining the type from scratch. | The passengers array binds actual traveler data ( |
On this page
- The Offers Flow