Price breakdown
Note: This section offers an early preview of a new capability launching with selected partners. Content will be updated as the feature is finalised. Please reach out to your Partnerships Manager any time to align on the latest specifications.
A journey can be priced in parts instead of a single number, and we return every part whenever possible β at search as fares, and on the order as order items. Neither response returns subtotals: it gives the priced components and the coverage of each, so a retailer can total them along whichever axis their interface needs β per passenger, per leg, per direction, per passenger type.
This page shows what the components are, how to aggregate them correctly, and how the two surfaces line up. Every example on it comes from one real reservation: a connections/find, the reservations/create that followed it, and the same reservation once confirmed.
Availability. The depth of the breakdown is the carrier's. It is described here at its most detailed β one component per passenger per segment, which is what SNCF returns today. Other carriers price more coarsely, and the same carrier can price differently per originβdestination. Always read the coverage from the response rather than assuming a shape. Order-side availability additionally depends on the retailer's RPN; see Order data.
The unit of pricing
One priced component appears under three names as it moves through the flow:
Stage | Where | What it is called |
|---|---|---|
Search | connections/find response | a fare, carrying its own offer_id |
Reservation | reservations/create request | an offer, referenced by that offer_id |
Order | reservations/create response, and every later read of the reservation | an order item |
The granularity is set by the carrier at search time. The downstream may subdivide it further or merge it. Every component states its own coverage β which passengers, which segments β so there is never a need to infer who or what it applies to.
Treat all ids in these responses as opaque strings. Coverage is carried in passenger_ids and segment_ids; never parse it out of an id.
The example used throughout
Nantes β Lausanne on 2026-09-24, changing in Paris: TGV INOUI 8814 Nantes β Paris Montparnasse, then LYRIA 9277 Paris Gare de Lyon β Lausanne. Three passengers β two adults and one child aged 11. The cheapest fare class, FARE-4 (Second Class β Cheapest), is the one reserved and confirmed, at β¬365.40.
Part 1 β Breakdown at search
The fare tree
A connection carries one or more standalone fares β each a priced option the carrier sells for the whole journey, covering every passenger and every segment, at a single price.
Each one holds its breakdown in included_fares: here, dependent fares, one per passenger per segment, each with its own price, services and conditions.
connection
βββ fare booking_type: standalone β reference this offer_id to reserve
βββ included_fares
βββ fare booking_type: dependent pax A, segment 1
βββ fare booking_type: dependent pax B, segment 1
βββ fare booking_type: dependent pax A, segment 2
βββ fare booking_type: dependent pax B, segment 2Selectability is a separate question from position in the tree. booking_type states only whether a fare can be selected on its own:
dependentβ cannot be selected on its own. Passing a dependentoffer_idtoreservations/createis rejected.standaloneβ can be selected on its own.
The first is breakdown only: a dependent fare explains the total and is never sold by itself. The second is the one to design the interface around, because a fare inside included_fares can itself be standalone β on a multi-carrier connection the breakdown is made of separately selectable fares. Whatever its booking_type, a fare listed in included_fares is one line of the total above it: take the breakdown from included_fares, and read booking_type only to know what may be selected alone.
Reserve by passing the offer_id of the fare listed on the connection; use its included fares to show the customer what makes up the total.
The standalone fare reserved here, trimmed:
{
"id": "SNCF-FRNTENAN-CHQLSLAU-2026-09-24T14:04-2026-09-24T21:39-0-FARE-4",
"type": "fares",
"attributes": {
"price": 36540,
"original_price": 36540,
"booking_type": "standalone",
"offer_id": "772a26f2-744a-481e-8d68-0a73482af5aa|0",
"passenger_ids": ["pax-11-1", "pax-33-2", "pax-33-3"],
"segment_ids": [
"SNCF-FRNTENAN-FRPARPRB-2026-09-24T14:04-2026-09-24T16:08-0-0",
"SNCF-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-0-1"
],
"offer_expires_at": "2026-09-15T03:18:45.955327Z",
"applied_passenger_types": [
{ "passenger_id": "pax-11-1", "passenger_type_id": "SNCF-PCIL" },
{ "passenger_id": "pax-33-2", "passenger_type_id": "SNCF-PNOS" },
{ "passenger_id": "pax-33-3", "passenger_type_id": "SNCF-PNOS" }
]
},
"relationships": {
"fare_class": { "data": { "id": "SNCF-FARE-4", "type": "fare_classes" } },
"included_fares": {
"data": [
{ "id": "β¦-FARE-4-C-pax-11-1-C-0", "type": "fares" },
{ "id": "β¦-FARE-4-C-pax-33-2-C-0", "type": "fares" },
{ "id": "β¦-FARE-4-C-pax-33-3-C-0", "type": "fares" },
{ "id": "β¦-FARE-4-C-pax-11-1-C-1", "type": "fares" },
{ "id": "β¦-FARE-4-C-pax-33-2-C-1", "type": "fares" },
{ "id": "β¦-FARE-4-C-pax-33-3-C-1", "type": "fares" }
]
},
"conditions": { "data": [{ "id": "SNCF-65cbβ¦8167a", "type": "fare_conditions" }] }
}
}
}
}
One of its included fares β the 11-year-old on the LYRIA leg:
{
"id": "SNCF-FRNTENAN-CHQLSLAU-2026-09-24T14:04-2026-09-24T21:39-0-FARE-4-C-pax-11-1-C-1",
"type": "fares",
"attributes": {
"price": 7140,
"original_price": 7140,
"booking_type": "dependent",
"offer_id": "772a26f2-744a-481e-8d68-0a73482af5aa|4",
"passenger_ids": ["pax-11-1"],
"segment_ids": ["SNCF-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-0-1"],
"services": [
{
"service_type_id": "SERVICE-TYPE-ADMISSION",
"passenger_ids": ["pax-11-1"],
"segment_ids": ["SNCF-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-0-1"]
},
{
"service_type_id": "SERVICE-TYPE-SEAT",
"passenger_ids": ["pax-11-1"],
"segment_ids": ["SNCF-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-0-1"]
}
],
"applied_passenger_types": [
{ "passenger_id": "pax-11-1", "passenger_type_id": "SNCF-PCIL" }
]
},
"relationships": {
"fare_class": { "data": { "id": "SNCF-FARE-4", "type": "fare_classes" } },
"conditions": { "data": [{ "id": "SNCF-721fβ¦6083e", "type": "fare_conditions" }] }
}
}
Delete
Collecting the components
- Take
data[].relationships.fares.datafor the connection's fares and resolve each id inincludedwheretypeisfares. - On a
standalonefare, read price for the journey total andrelationships.included_faresfor the detail. - Resolve each fare listed in
included_fares. Readprice,passenger_ids,segment_ids,services,applied_passenger_typesandrelationships.conditionsfrom the fare itself. - Resolve
passenger_type_idin included underpassenger_typesfor a label βSNCF-PCILis Child (4β11),SNCF-PNOSis Adult (26β59). Segments, fare classes and conditions resolve fromincludedthe same way. - Pass the
offer_idof the connection-level standalone fare toreservations/create.
Prices are integers in the currency's fractional unit β 7140 is β¬71.40 with meta.currency: "EUR". See Currencies for the full convention, including KRW and JPY, which are returned as full units.
Aggregating price
Every total on a breakdown is a sum over the included fares, selected by coverage.
Per passenger β sum the fares whose passenger_ids contains that passenger:
Passenger | Type | TGV INOUI 8814 | LYRIA 9277 | Passenger total |
|---|---|---|---|---|
|
| β¬18.00 | β¬71.40 | β¬89.40 |
|
| β¬36.00 | β¬102.00 | β¬138.00 |
|
| β¬36.00 | β¬102.00 | β¬138.00 |
Per segment | β¬90.00 | β¬275.40 | β¬365.40 |
Per segment β sum the fares whose segment_ids contains that segment (the bottom row above).
Per passenger type β group by applied_passenger_types[].passenger_type_id: β¬276.00 for the two adults, β¬89.40 for the child passenger.
Per direction β each direction is its own connection with its own fare tree. Read leg_type (outbound / inbound) on the connection and total each separately.
Whole journey β the standalone fare's price. No summing is needed for this one.
The check that should always pass: the included fares sum exactly to the price of the fare above them. On this connection, for each of the four standalone fares:
Standalone fare | Included fares | Sum | Standalone price |
|---|---|---|---|
| 6 | β¬365.40 | β¬365.40 |
| 6 | β¬397.90 | β¬397.90 |
| 6 | β¬507.80 | β¬507.80 |
| 6 | β¬919.00 | β¬919.00 |
Two things that cannot be derived
A per-passenger price where the carrier did not price one. Where a fare covers several passengers, dividing its price by the number of passenger_ids gives a number the customer cannot be held to. Only split a total where included fares actually provide the split.
A price per service. services[] states what a fare buys, not what each part costs. An included fare covering ADMISSION and SEAT is one price for both β there is no separate seat price to show.
Part 2 β The same breakdown on the order
Once the search becomes a reservation, the components come back as order items, on the reservations/create response and on every later read of the reservation β no extra call. The full field reference is in Order data β Retailer API guide; this section covers only how it lines up with the breakdown aggregated at search.
Reserving the FARE-4 standalone fare above produced reservation RTT25XGX with six items β one per passenger per segment, the same six components, the same six prices:
Item | Covers | Type | Segment | Fare class | Condition | Price |
|---|---|---|---|---|---|---|
1ec0091cβ¦ | passenger-1 | PNOS | TGV INOUI 8814 | FARE-4 | CONDITION-6 | β¬36.00 |
39b1b77bβ¦ | passenger-2 | PNOS | TGV INOUI 8814 | FARE-4 | CONDITION-6 | β¬36.00 |
6c6071c6β¦ | passenger-3 | PCIL | TGV INOUI 8814 | FARE-4 | CONDITION-6 | β¬18.00 |
d259b627β¦ | passenger-1 | PNOS | LYRIA 9277 | FARE-5 | CONDITION-20 | β¬102.00 |
fcb287f1β¦ | passenger-2 | PNOS | LYRIA 9277 | FARE-5 | CONDITION-20 | β¬102.00 |
e472d672β¦ | passenger-3 | PCIL | LYRIA 9277 | FARE-5 | CONDITION-20 | β¬71.40 |
Order total | β¬365.40 |
The child passenger's LYRIA item β the same β¬71.40 returned as an included fare at search:
{
"id": "e472d672-9313-41d8-a253-f9a0c90d8dff",
"type": "order_items",
"attributes": {
"status": "reserved",
"passenger_ids": ["passenger-3"],
"segment_ids": ["STLY-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-1"],
"price": 7140,
"original_price": 7140,
"services": [
{
"service_type_id": "ADMISSION",
"passenger_ids": ["passenger-3"],
"segment_ids": ["STLY-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-1"]
},
{
"service_type_id": "SEAT",
"passenger_ids": ["passenger-3"],
"segment_ids": ["STLY-FRPARPRG-CHQLSLAU-2026-09-24T17:32-2026-09-24T21:39-1"]
}
],
"applied_passenger_types": [
{ "id": "SNCF-PCIL", "passenger_id": "passenger-3" }
]
},
"relationships": {
"fare_class": { "data": { "id": "SNCF-FARE-5", "type": "fare_classes" } },
"conditions": { "data": [{ "id": "8e0f02d0-016d-46ff-ae9a-c3429dbe2732", "type": "conditions" }] }
}
}
}
}
Every aggregation from Part 1 works unchanged here β same coverage fields, same rules, one less level of nesting. The sum check becomes: items always sum to data.attributes.total_price.
The components can, however, arrive merged. A carrier that priced per passenger per segment at search may return coarser items once the reservation exists β one item covering several passengers, or several segments. This chain does not show it, so read the shape from each item's own passenger_ids and segment_ids rather than carrying the search breakdown's shape across.
Read fare class from the item, not the reservation
The reservation's top-level fare_class is FARE-4 β the fare class searched and reserved. But the LYRIA items carry FARE-5. That is not an inconsistency: SNCF priced the Nantes leg as Second Class β Cheapest and the Lausanne leg as Second Class β SemiFlexi, and FARE-4 is the name of the combination. The per-item value is the one that describes what that leg actually is, which is why the conditions differ with it.
At search the included fares all repeat the fare class of the fare above them, so this only becomes visible on the order. Label each line from the item's own fare class, not from the reservation's.
Status, and when the numbers settle
status on an item tracks the reservation:
Reservation state | Item status | order_version |
|---|---|---|
created (after reservations/create) | reserved | 1 |
confirmed | confirmed | 2 |
Prices do not move across that transition β total_price and every item price are identical at create and after confirmation. order_version increments when the order changes.
Order items are keyed by id
An item's id is stable across reads and across the reservation's lifecycle: the same six ids come back at reservations/create, at reservations/confirm and on the confirmed reservation, and meta.actions references items by that id. Store it alongside any breakdown that will be refreshed later, and resolve each line by id rather than by its position in order_items.
Reconciling a search breakdown against the order
Where a breakdown shown at search is stored and later matched line for line against the order, note that the identifiers change at the reservation boundary. Neither ids nor list positions carry over:
At search | On the order | |
|---|---|---|
Segment | SNCF-FRNTENAN-FRPARPRB-2026-09-24T14:04-2026-09-24T16:08-0-0 | STIO-FRNTENAN-FRPARPRB-2026-09-24T14:04-2026-09-24T16:08-0 |
Passenger | pax-11-1 (the 11-year-old) | passenger-3 |
Component | fare id + offer_id | order item UUID |
Segment ids are rebuilt around the operating carrier (STIO, STLY) rather than the marketing carrier (SNCF). Passenger ids are reservation-scoped and follow the order in which passengers were sent to reservations/create β which need not be the order searched in, as it is not here.
So match on what both sides state plainly:
- Segment β departure station, arrival station, departure time and arrival time. All four are on the segment objects in included on both responses.
- Passenger β the passengers sent to reservations/create, in the order they were sent, become passenger-1β¦n; applied_passenger_types confirms the type of each.
- Then the amount β where the order kept the search granularity, one included fare pairs with one order item per (passenger, segment), at the same price. Where items arrived merged, several included fares sum to one item: compare against the coverage the item states rather than line by line.
On this page
- Price breakdown