diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 66c3fba..0000000 --- a/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -out/ -.git/ -.jj/ - -.gitignore diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index daecc85..0000000 --- a/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM python:3.13-slim AS base -# FROM ghcr.io/astral-sh/uv:0.8-debian-slim AS builder -# -# WORKDIR /app -# -# COPY pyproject.toml README.md uv.lock ./ -# -# RUN uv sync --frozen -# -# COPY . /app - -FROM base AS builder -COPY --from=ghcr.io/astral-sh/uv:0.8 /uv /bin/uv -ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy -WORKDIR /app -COPY uv.lock pyproject.toml /app/ -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-install-project --no-dev -COPY . /app -RUN --mount=type=cache,target=/root/.cache/uv \ - uv sync --frozen --no-dev - -# TODO: Run as non-root user (but careful with output directory ownership) -FROM base AS runtime - -WORKDIR /app -ENV PATH="/app/.venv/bin:$PATH" - -COPY --from=builder /app /app - -ENTRYPOINT ["nightjet"] diff --git a/README.md b/README.md index d430769..f9f63c4 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,3551 @@ -# Nightjetter +# Nightjet API -A search through all the available prices for a single ÖBB Nightjet route from A to B. +Most from: `https://martinlangbecker.github.io/night-train-apis/?urls.primaryName=NightJet#/Initialize/post_init_start` -Can be used to continuously monitor prices and notify the user on changes, -or for a single search for all available prices. +`curl -X 'POST' 'https://www.nightjet.com/nj-booking-ocp/init/start' -H 'accept: application/json' -H 'Referer: https://www.nightjet.com/' -H 'Content-Type: application/json' -d '{ "lang": "de" }' > resp/init_resp.json` -Comes with a nice Python CLI, or could be imported as a Python library for programmatic use. +- start with init request to grab "token" key from response +- the the token (jwt) in connection search as 'x-token' header -## The CLI +`eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1MzcwMjUsInB1YmxpY0lkIjoiYWZiZjM3NzBjZGU2NDllNTg2ZGJiMWYyOTIxN2NkYWUifQ.FqZMBeORf1SvXzHBOgwvPoTi7xPhbq4tbIg8KMSX5eI` -It comes with a `typer`-supported CLI which you can use to craft your search. -Most options should be pretty self-explanatory, but a few notes: +## connection request -- `monitor-mode` switches between a single search or the app running continuously until you turn it off. -- Any files created by the application are put under the `base-output-directory`, so make sure it has write permissions there. -- I am not sure how important the `birthdate` is, but included it since Nightjet may give discounts for young people or seniors? -- You can use the price snapshots, if you decide to create them with `dump-price-snapshot`, to create pretty time series graphs -- Currently, we _only_ support `ntfy`, as well as the official `ntfy` server as the notification channel +WORKING request -```help - Usage: nightjet [OPTIONS] TRAVEL_DATE +```bash +curl 'https://www.nightjet.com/nj-booking-ocp/connection/8096003/8796001/2025-10-14' \ + -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0' \ + -H 'Accept: application/json' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Accept-Encoding: gzip, deflate, br, zstd' \ + -H 'Referer: https://www.nightjet.com/en/ticket-buchen/' \ + -H 'x-token: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1NDEwODUsInB1YmxpY0lkIjoiN2JjYTBhMGZlNjIyNDVlNGIyZWVlYjQzN2NkM2RjMzgifQ._1VxBxOcxQagYg7DpX-gQprU7i6GT7CBOb8SHixJf6I' \ + -H 'Connection: keep-alive' \ + -H 'Cookie: 34d5d3b7c2b81811ce5d8c490a20f30f=2c79d5b872ea8e0ebceca7def7e259d3; NSC_ESNS=21cebc26-33db-189a-9678-daf5b9e7e029_1676131999_2540214590_00000000000566328293' \ + -H 'Sec-Fetch-Dest: empty' \ + -H 'Sec-Fetch-Mode: cors' \ + -H 'Sec-Fetch-Site: same-origin' \ + -H 'DNT: 1' \ + -H 'Sec-GPC: 1' \ + -H 'Priority: u=4' \ + -H 'Pragma: no-cache' \ + -H 'Cache-Control: no-cache'``` -╭─ Arguments ─────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ * travel_date TEXT Travel day to search from. (YYYY-MM-DD) [default: None] [required] │ -╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ -╭─ Options ───────────────────────────────────────────────────────────────────────────────────────────────────────────╮ -│ --start-station INTEGER Departure station number. (default: Berlin Hbf) │ -│ [default: 8096003] │ -│ --end-station INTEGER Destination station number. (default: Paris Est) │ -│ [default: 8796001] │ -│ --birthdate TEXT Traveller birthdate, may be important for │ -│ discounts. (YYYY-MM-DD) │ -│ [default: 1990-01-01] │ -│ --notification-channel TEXT ntfy channel to inform user on. │ -│ [default: nightjet-price-notifier] │ -│ --monitor-mode --no-monitor-mode Run queries repeatedly over time. If False only │ -│ runs a single query (oneshot mode). │ -│ [default: monitor-mode] │ -│ --monitor-frequency INTEGER How often to run price queries if in monitoring │ -│ mode, in seconds. │ -│ [default: 3600] │ -│ --base-output-directory PATH Directory in which to output all result files. │ -│ [default: out] │ -│ --lowest-prices-filename TEXT Filename for collecting lowest found prices. │ -│ [default: lowest.csv] │ -│ --price-snapshot-pattern TEXT Filename pattern for saving all prices of each │ -│ query. Takes %%DATE%% as pattern to replace with │ -│ current unix timestamp. │ -│ [default: all_prices_%%DATE%%.csv] │ -│ --dump-price-snapshot --no-dump-price-snapshot Dump _all_ queried prices into a timestamped csv │ -│ file. │ -│ [default: dump-price-snapshot] │ -│ --install-completion Install completion for the current shell. │ -│ --show-completion Show completion for the current shell, to copy it │ -│ or customize the installation. │ -│ --help Show this message and exit. │ -╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +### connection response + +```json +{ + "connections": [ + { + "from": { + "name": "Berlin Hbf", + "number": "8011160" + }, + "to": { + "name": "Paris Est", + "number": "8700011" + }, + "trains": [ + { + "train": "NJ 40424", + "departure": { + "utc": 1760461680000, + "local": "2025-10-14T19:08:00" + }, + "arrival": { + "utc": 1760513880000, + "local": "2025-10-15T09:38:00" + }, + "trainType": "regular", + "seatAsIC": false + } + ] + }, + { + "from": { + "name": "Berlin Hbf", + "number": "8011160" + }, + "to": { + "name": "Paris Est", + "number": "8700011" + }, + "trains": [ + { + "train": "NJ 40424", + "departure": { + "utc": 1760634480000, + "local": "2025-10-16T19:08:00" + }, + "arrival": { + "utc": 1760686680000, + "local": "2025-10-17T09:38:00" + }, + "trainType": "regular", + "seatAsIC": false + } + ] + }, + { + "from": { + "name": "Berlin Hbf", + "number": "8011160" + }, + "to": { + "name": "Paris Est", + "number": "8700011" + }, + "trains": [ + { + "train": "NJ 40424", + "departure": { + "utc": 1760893680000, + "local": "2025-10-19T19:08:00" + }, + "arrival": { + "utc": 1760945880000, + "local": "2025-10-20T09:38:00" + }, + "trainType": "regular", + "seatAsIC": false + } + ] + } + ] +} ``` -## Future +## booking Request -This tool does (mostly did) what I needed it to do. -That means I don't have a big drive to work on, change and add to the current codebase. -Nevertheless, feel welcome to file an issue if you noticed something, or have a feature request. -PRs are of course also always welcome. +example request body -Here are a bunch of things I think should definitely be considered for further work: +```json +{ + "njFrom": 8011160, + //"njArr": 1697307840000, + "njDep": 1760461680000, + "njTo": 8700011, + //"finalFrom": 8000199, + //"finalTo": 5696001, + "maxChanges": 0, + "filter": { + // "njTrain": "NJ 40491", + "njDeparture": 1760461680000, + // "njRoute": [ + // { + // "arrival": "2024-06-20T20:21:00+02:00", + // "arrivalAccessExitAmendable": true, + // "arrivalPlatform": "3", + // "departure": "2024-06-20T20:21:00+02:00", + // "departureAccessExitAmendable": true, + // "departurePlatform": "3", + // "departurePlatformRT": "3", + // "gpsCoordinates": [ + // 53455910, + // 9991699 + // ], + // "name": "Hamburg-Harburg", + // "stationNumber": 8000147, + // "stopState": null + // } + // ], + // "njProduct": "ARES Sparschiene Nachtverkehr", + // "privateVariationCount": null + }, + "objects": [ + { + "birthDate": "1993-10-13", + "cards": [ + 127 + ], + "gender": "female", + "type": "person" + } + ], + "relations": [ + { + "lhs": 1, + "relationType": "attendant_of", + "rhs": 0 + } + ], + "lang": "de" +} +``` -1. The code is not very organized, and everything is one big file. - Perhaps this could be extracted into the individual areas of operation a bit better. +WORKING request body: -2. Additional patterns for the price snapshots. We currently only consider `%%DATE%%` as a valid substitution. - It is substituted into a unix timestamp. More flexibility here could be nice. - Perhaps also allow the substitution in other paths passed to the cli. +```bash +curl 'https://www.nightjet.com/nj-booking-ocp/offer/get' \ + --compressed \ + -X POST \ + -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0' \ + -H 'Accept: application/json' \ + -H 'Accept-Language: en-US,en;q=0.5' \ + -H 'Accept-Encoding: gzip, deflate, br, zstd' \ + -H 'Referer: https://www.nightjet.com/en/ticket-buchen/' \ + -H 'content-type: application/json' \ + -H 'x-token: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1NDEwODUsInB1YmxpY0lkIjoiN2JjYTBhMGZlNjIyNDVlNGIyZWVlYjQzN2NkM2RjMzgifQ._1VxBxOcxQagYg7DpX-gQprU7i6GT7CBOb8SHixJf6I' \ + -H 'Origin: https://www.nightjet.com' \ + -H 'Connection: keep-alive' \ + -H 'Cookie: 34d5d3b7c2b81811ce5d8c490a20f30f=2c79d5b872ea8e0ebceca7def7e259d3; NSC_ESNS=21b97221-349f-189a-9678-daf5b9e7e029_1525529053_2925697282_00000000013450580068' \ + -H 'Sec-Fetch-Dest: empty' \ + -H 'Sec-Fetch-Mode: cors' \ + -H 'Sec-Fetch-Site: same-origin' \ + -H 'DNT: 1' \ + -H 'Sec-GPC: 1' \ + -H 'Priority: u=4' \ + --data-raw '{"njFrom":8011160,"njDep":1760461680000,"njTo":8700011,"maxChanges":0,"connections":1,"filter":{"njTrain":"NJ 40424","njDeparture":1760461680000},"objects":[{"type":"person","birthDate":"1995-08-11","cards":[]}],"relations":[],"lang":"en"}' +``` -3. The stations are only considered as internal numbers (`int` types). This would be a first QoL change, - to automatically look up the correct station number for whatever is passed in. +request data -4. We don't map the trains, stations and journeys as internal objects. - Instead they are all passed around as dicts and JSON objects. - This could definitely be improved for better maintainability. +```json +{ + "njFrom": 8011160, + "njDep": 1760461680000, + "njTo": 8700011, + "maxChanges": 0, + "connections": 1, + "filter": { + "njTrain": "NJ 40424", + "njDeparture": 1760461680000 + }, + "objects": [ + { + "type": "person", + "birthDate": "1995-08-11", + "cards": [] + } + ], + "relations": [], + "lang": "en" +} +``` -5. Support more notification channels. This should be self-explanatory, and could probably be pretty - easily be improved with a library like [apprise](https://github.com/caronc/apprise?tab=readme-ov-file#developer-api-usage). +### booking response + +```json +{ + "result": [ + { + "connections": [ + { + "sections": [ + { + "departureTimestamp": "2025-10-14T19:08:00+02:00", + "arrivalTimestamp": "2025-10-15T09:38:00+02:00", + "passlist": [ + { + "name": "Berlin Hbf", + "stationNumber": 8011160, + "departure": "2025-10-14T19:08:00+02:00", + "departureAccessExitAmendable": true, + "departurePlatform": "13", + "gpsCoordinates": [ + 52525589, + 13369549 + ] + }, + { + "name": "Göttingen", + "stationNumber": 8000128, + "departure": "2025-10-14T21:37:00+02:00", + "arrival": "2025-10-14T21:35:00+02:00", + "arrivalAccessExitAmendable": true, + "arrivalPlatform": "10", + "departureAccessExitAmendable": true, + "departurePlatform": "10", + "gpsCoordinates": [ + 51536812, + 9926069 + ] + }, + { + "name": "Kassel-Wilhelmshöhe", + "stationNumber": 8003200, + "departure": "2025-10-14T22:08:00+02:00", + "arrival": "2025-10-14T21:58:00+02:00", + "arrivalAccessExitAmendable": true, + "arrivalPlatform": "1", + "departureAccessExitAmendable": true, + "departurePlatform": "1", + "gpsCoordinates": [ + 51312558, + 9447114 + ] + }, + { + "name": "Frankfurt(Main)Süd", + "stationNumber": 8002041, + "departure": "2025-10-15T00:26:00+02:00", + "arrival": "2025-10-15T00:22:00+02:00", + "arrivalAccessExitAmendable": true, + "arrivalPlatform": "6", + "departureAccessExitAmendable": true, + "departurePlatform": "6", + "gpsCoordinates": [ + 50099365, + 8686456 + ] + }, + { + "name": "Mannheim Hbf", + "stationNumber": 8000244, + "departure": "2025-10-15T03:40:00+02:00", + "arrival": "2025-10-15T01:34:00+02:00", + "arrivalAccessExitAmendable": false, + "departureAccessExitAmendable": false, + "gpsCoordinates": [ + 49479352, + 8468917 + ] + }, + { + "name": "Forbach Frontière de l'État", + "stationNumber": 8002021, + "departure": "2025-10-15T05:11:00+02:00", + "arrival": "2025-10-15T05:11:00+02:00", + "arrivalAccessExitAmendable": false, + "departureAccessExitAmendable": false, + "gpsCoordinates": [ + 49214089, + 6944311 + ] + }, + { + "name": "Paris Est", + "stationNumber": 8700011, + "arrival": "2025-10-15T09:38:00+02:00", + "arrivalAccessExitAmendable": true, + "stopState": [ + "ARR_PROGNOSED" + ], + "gpsCoordinates": [ + 48876976, + 2359120 + ] + } + ], + "attributes": [ + { + "attributeType": "operator", + "range": [ + 0, + 6 + ], + "value": "DPN", + "svalue": "DPN", + "lvalue": "Nahreisezug" + }, + { + "attributeType": "operator_id", + "range": [ + 0, + 6 + ], + "value": "81" + }, + { + "attributeType": "pclass", + "range": [ + 0, + 6 + ], + "value": 3 + }, + { + "attributeType": "intcat", + "range": [ + 0, + 6 + ], + "value": "NJ" + }, + { + "attributeType": "cat", + "range": [ + 0, + 6 + ], + "value": "NJ ", + "svalue": "NJ", + "lvalue": "nightjet", + "code": "3" + }, + { + "attributeType": "name", + "range": [ + 0, + 6 + ], + "value": "NJ 40424" + }, + { + "attributeType": "num", + "range": [ + 0, + 6 + ], + "value": "40424" + }, + { + "attributeType": "intnum", + "range": [ + 0, + 6 + ], + "value": 40424 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Subject to compulsory reservation", + "code": "RP", + "priority": 1 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "space for wheelchairs", + "code": "RO", + "priority": 150 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Wheelchair space - For advance notification, call +43 5 1717", + "code": "OA", + "priority": 150 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "WC accessible for wheelchair", + "code": "OC", + "priority": 150 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Bicycles conveyed - subject to reservation", + "code": "FR", + "priority": 250 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Number of bicycles conveyed limited", + "code": "FK", + "priority": 250 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "2nd class only seated accommodation", + "code": "J2", + "priority": 300 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "ÖBB Nightjet (www.nightjet.com)", + "code": "OJ", + "priority": 320 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "IRT Integrated Reservation Ticket", + "code": "CT", + "priority": 320 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Global price", + "code": "GP", + "priority": 320 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Sleeping-car", + "code": "SW", + "priority": 400 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Couchettes", + "code": "LW", + "priority": 400 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "snacks and beverages available from sleeper/couchette attendant", + "code": "MN", + "priority": 450 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Timetable is subject to change or adjustment", + "code": "50", + "priority": 921 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "Hinweis: Fahrzeiten können sich noch ändern.", + "code": "s1", + "priority": 921 + }, + { + "attributeType": "generic", + "range": [ + 0, + 6 + ], + "value": "nightjet", + "code": "ZN", + "priority": 100 + }, + { + "attributeType": "info", + "range": [ + 0, + 6 + ], + "value": "Paris Est", + "code": "RL" + }, + { + "attributeType": "dir", + "range": [ + 0, + 6 + ], + "value": "Paris Est" + } + ], + "i": 0, + "transportType": "journey" + } + ], + "offers": [ + { + "name": "Standard-Ticket inkl. Reservierung", + "productType": "CONNECTION", + "partialOffer": false, + "productDetails": [ + { + "scope": [ + [ + 0, + 0, + 0, + 6 + ] + ], + "title": "ARES Standard-Ticket Nachtverkehr", + "name": { + "de": "Standard-Ticket inkl. Reservierung", + "en": "Standard-Ticket incl. Reservation", + "it": "Standard-Ticket prenotazione incl." + }, + "validityPeriodFrom": "2025-10-14T19:08:00+02:00", + "validityPeriodTo": "2025-10-15T09:38:00+02:00", + "globallyPriced": true, + "objects": [ + { + "index": 0, + "priceClass2": 89.9 + } + ], + "prodGroupLabels": [ + "918 1 Reservierung Default", + "Vollstorno", + "Zugbindung", + "eineFahrt", + "featured" + ], + "validityType": "oneway" + } + ], + "reservation": { + "reservationSegments": [ + { + "scope": [ + 0, + 0, + 0, + 6 + ], + "compartments": [ + { + "externalIdentifier": "couchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 3756464, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 4 Personen", + "en": "Compartment for 4 passengers", + "fr": ".", + "it": "scompartimento per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "couchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 134.9, + "reservability": "RP" + } + ], + "id": 3756468, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 6 Personen", + "en": "Compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 45, + "capacity": 6 + }, + { + "externalIdentifier": "singlePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 664.9, + "reservability": "RP" + } + ], + "id": 3756876, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", + "en": "Compartment for 1 passenger with shower/WC (Single plus)", + "fr": ".", + "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" + }, + "spotLocations": [], + "surchargePrice": 575, + "capacity": 1 + }, + { + "externalIdentifier": "privateCouchette", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 564.9 + } + ] + } + ], + "surchargePrice": 475 + } + ], + "id": 5000057, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Liegewagen für bis zu 6 Personen", + "en": "Private compartment for up to 6 passengers in a couchette coach", + "fr": ".", + "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "privateSeat", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 424.9 + } + ] + } + ], + "surchargePrice": 335 + } + ], + "id": 5000059, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Sitzwagen 2. Klasse", + "en": "Private compartment in a 2nd class seated coach", + "fr": ".", + "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "femaleCouchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 5000068, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 4 Personen", + "en": "Ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 134.9, + "reservability": "RP" + } + ], + "id": 5000069, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 6 Personen", + "en": "Ladies only compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento donna per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + } + ], + "surchargePrice": 45, + "capacity": 6 + }, + { + "externalIdentifier": "sideCorridorCoach_2", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "objects": [ + { + "index": 0, + "price": 89.9, + "reservability": "RP" + } + ], + "id": 5000075, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Sitzplatz 2. Klasse", + "en": "Seat 2nd class", + "fr": ".", + "it": "Posto a sedere in 2a classe" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "möglichst am Fenster", + "en": "if possible at the window", + "fr": "möglichst am Fenster", + "it": "preferibilmente finestrino" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "möglichst in der Mitte", + "en": "if possible in the center", + "fr": "möglichst in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "möglichst am Gang", + "en": "if possible at the corridor", + "fr": "möglichst am Gang", + "it": "preferibilmente corridoio" + } + }, + { + "param": "NECESSARILY_WINDOW_LOWER", + "name": { + "de": "unbedingt am Fenster", + "en": "absolutely situated at the window", + "fr": "unbedingt am Fenster", + "it": "assolutamente finestrino" + } + } + ], + "capacity": 6 + }, + { + "externalIdentifier": "privateCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 564.9 + } + ] + } + ], + "surchargePrice": 475 + } + ], + "id": 5000085, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", + "en": "Comfort private compartment in a couchette coach for up to 4 passengers", + "fr": ".", + "it": "Scompartimento privato comfort per 4 persone al massimo" + }, + "spotLocations": [], + "capacity": 4, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "couchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 5000107, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil comfort für 4 Personen", + "en": "Comfort compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 5000108, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil comfort für 4 Personen", + "en": "Comfort ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 264.9, + "reservability": "RP" + } + ], + "id": 5000151, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 175, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 264.9, + "reservability": "RP" + } + ], + "id": 5000153, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 175, + "capacity": 3 + }, + { + "externalIdentifier": "mixedDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 329.9, + "reservability": "RP" + } + ], + "id": 5000160, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 240, + "capacity": 2 + }, + { + "externalIdentifier": "femaleDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 329.9, + "reservability": "RP" + } + ], + "id": 5000162, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 240, + "capacity": 2 + }, + { + "externalIdentifier": "single", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 514.9, + "reservability": "RP" + } + ], + "id": 5000198, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person (Single)", + "en": "Compartment for 1 passenger (Single)", + "fr": ".", + "it": "Scompartimento per 1 persona (Single)" + }, + "spotLocations": [], + "surchargePrice": 425, + "capacity": 1 + }, + { + "externalIdentifier": "femaleDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 259.9, + "reservability": "RP" + } + ], + "id": 5000200, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen (Double)", + "en": "Ladies only compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento donna per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 170, + "capacity": 2 + }, + { + "externalIdentifier": "mixedDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 259.9, + "reservability": "RP" + } + ], + "id": 5000201, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen (Double)", + "en": "Compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 170, + "capacity": 2 + }, + { + "externalIdentifier": "femaleT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 204.9, + "reservability": "RP" + } + ], + "id": 5000202, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen (Triple)", + "en": "Ladies only compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento donna per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 115, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 204.9, + "reservability": "RP" + } + ], + "id": 5000204, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen (Triple)", + "en": "Compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 115, + "capacity": 3 + } + ], + "reservability": "RP" + } + ], + "priceClass2": 0 + }, + "priceClass2": 89.9, + "refCards": [], + "co2Savings": 252.89, + "outputTypes": [ + "MOBILE", + "PDF", + "SECURITY_PAPER" + ], + "prodGroupLabels": [ + "918 1 Reservierung Default", + "Vollstorno", + "Zugbindung", + "eineFahrt", + "featured" + ], + "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", + "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", + "validityType": "oneway", + "rideBound": true, + "singleVariety": true, + "reservationMandatory": true + }, + { + "name": "Sparschiene Komfort inkl. Reservierung", + "productType": "CONNECTION", + "partialOffer": false, + "productDetails": [ + { + "scope": [ + [ + 0, + 0, + 0, + 6 + ] + ], + "title": "ARES Sparschiene Komfort Nachtverkehr", + "name": { + "de": "Sparschiene Komfort inkl. Reservierung", + "en": "Sparschiene Komfort incl. Reservation", + "it": "Sparschiene Komfort prenotazione incl." + }, + "validityPeriodFrom": "2025-10-14T19:08:00+02:00", + "validityPeriodTo": "2025-10-15T09:38:00+02:00", + "globallyPriced": true, + "objects": [ + { + "index": 0, + "priceClass2": 64.9 + } + ], + "prodGroupLabels": [ + "Zugbindung", + "eineFahrt", + "featured", + "komfortticketStorno" + ], + "validityType": "oneway" + } + ], + "reservation": { + "reservationSegments": [ + { + "scope": [ + 0, + 0, + 0, + 6 + ], + "compartments": [ + { + "externalIdentifier": "couchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 129.9, + "reservability": "RP" + } + ], + "id": 3756464, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 4 Personen", + "en": "Compartment for 4 passengers", + "fr": ".", + "it": "scompartimento per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "couchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 109.9, + "reservability": "RP" + } + ], + "id": 3756468, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 6 Personen", + "en": "Compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 45, + "capacity": 6 + }, + { + "externalIdentifier": "singlePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 494.9, + "reservability": "RP" + } + ], + "id": 3756876, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", + "en": "Compartment for 1 passenger with shower/WC (Single plus)", + "fr": ".", + "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" + }, + "spotLocations": [], + "surchargePrice": 430, + "capacity": 1 + }, + { + "externalIdentifier": "privateCouchette", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 519.9 + } + ] + } + ], + "surchargePrice": 455 + } + ], + "id": 5000057, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Liegewagen für bis zu 6 Personen", + "en": "Private compartment for up to 6 passengers in a couchette coach", + "fr": ".", + "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "privateSeat", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 319.9 + } + ] + } + ], + "surchargePrice": 255 + } + ], + "id": 5000059, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Sitzwagen 2. Klasse", + "en": "Private compartment in a 2nd class seated coach", + "fr": ".", + "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "femaleCouchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 129.9, + "reservability": "RP" + } + ], + "id": 5000068, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 4 Personen", + "en": "Ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 109.9, + "reservability": "RP" + } + ], + "id": 5000069, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 6 Personen", + "en": "Ladies only compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento donna per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + } + ], + "surchargePrice": 45, + "capacity": 6 + }, + { + "externalIdentifier": "sideCorridorCoach_2", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "objects": [ + { + "index": 0, + "price": 64.9, + "reservability": "RP" + } + ], + "id": 5000075, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Sitzplatz 2. Klasse", + "en": "Seat 2nd class", + "fr": ".", + "it": "Posto a sedere in 2a classe" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "möglichst am Fenster", + "en": "if possible at the window", + "fr": "möglichst am Fenster", + "it": "preferibilmente finestrino" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "möglichst in der Mitte", + "en": "if possible in the center", + "fr": "möglichst in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "möglichst am Gang", + "en": "if possible at the corridor", + "fr": "möglichst am Gang", + "it": "preferibilmente corridoio" + } + }, + { + "param": "NECESSARILY_WINDOW_LOWER", + "name": { + "de": "unbedingt am Fenster", + "en": "absolutely situated at the window", + "fr": "unbedingt am Fenster", + "it": "assolutamente finestrino" + } + } + ], + "capacity": 6 + }, + { + "externalIdentifier": "privateCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 519.9 + } + ] + } + ], + "surchargePrice": 455 + } + ], + "id": 5000085, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", + "en": "Comfort private compartment in a couchette coach for up to 4 passengers", + "fr": ".", + "it": "Scompartimento privato comfort per 4 persone al massimo" + }, + "spotLocations": [], + "capacity": 4, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "couchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 129.9, + "reservability": "RP" + } + ], + "id": 5000107, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil comfort für 4 Personen", + "en": "Comfort compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 129.9, + "reservability": "RP" + } + ], + "id": 5000108, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil comfort für 4 Personen", + "en": "Comfort ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 65, + "capacity": 4 + }, + { + "externalIdentifier": "femaleT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 199.9, + "reservability": "RP" + } + ], + "id": 5000151, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 135, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 199.9, + "reservability": "RP" + } + ], + "id": 5000153, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 135, + "capacity": 3 + }, + { + "externalIdentifier": "mixedDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 249.9, + "reservability": "RP" + } + ], + "id": 5000160, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 185, + "capacity": 2 + }, + { + "externalIdentifier": "femaleDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 249.9, + "reservability": "RP" + } + ], + "id": 5000162, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 185, + "capacity": 2 + }, + { + "externalIdentifier": "single", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 384.9, + "reservability": "RP" + } + ], + "id": 5000198, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person (Single)", + "en": "Compartment for 1 passenger (Single)", + "fr": ".", + "it": "Scompartimento per 1 persona (Single)" + }, + "spotLocations": [], + "surchargePrice": 320, + "capacity": 1 + }, + { + "externalIdentifier": "femaleDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 194.9, + "reservability": "RP" + } + ], + "id": 5000200, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen (Double)", + "en": "Ladies only compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento donna per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 130, + "capacity": 2 + }, + { + "externalIdentifier": "mixedDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 194.9, + "reservability": "RP" + } + ], + "id": 5000201, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen (Double)", + "en": "Compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 130, + "capacity": 2 + }, + { + "externalIdentifier": "femaleT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 169.9, + "reservability": "RP" + } + ], + "id": 5000202, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen (Triple)", + "en": "Ladies only compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento donna per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 105, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 169.9, + "reservability": "RP" + } + ], + "id": 5000204, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen (Triple)", + "en": "Compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 105, + "capacity": 3 + } + ], + "reservability": "RP" + } + ], + "priceClass2": 0 + }, + "priceClass2": 64.9, + "refCards": [], + "co2Savings": 252.89, + "outputTypes": [ + "MOBILE", + "PDF", + "SECURITY_PAPER" + ], + "prodGroupLabels": [ + "Zugbindung", + "eineFahrt", + "featured", + "komfortticketStorno" + ], + "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", + "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", + "validityType": "oneway", + "rideBound": true, + "singleVariety": true, + "reservationMandatory": true + }, + { + "name": "Sparschiene inkl. Reservierung", + "productType": "CONNECTION", + "partialOffer": false, + "productDetails": [ + { + "scope": [ + [ + 0, + 0, + 0, + 6 + ] + ], + "title": "ARES Sparschiene Nachtverkehr", + "name": { + "de": "Sparschiene inkl. Reservierung", + "en": "Sparschiene incl. Reservation", + "it": "Sparschiene prenotazione incl." + }, + "validityPeriodFrom": "2025-10-14T19:08:00+02:00", + "validityPeriodTo": "2025-10-15T09:38:00+02:00", + "globallyPriced": true, + "objects": [ + { + "index": 0, + "priceClass2": 44.9 + } + ], + "prodGroupLabels": [ + "Kein Storno", + "Zugbindung", + "eineFahrt", + "featured", + "nightjetSparschiene" + ], + "validityType": "oneway" + } + ], + "reservation": { + "reservationSegments": [ + { + "scope": [ + 0, + 0, + 0, + 6 + ], + "compartments": [ + { + "externalIdentifier": "couchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 114.9, + "reservability": "RP" + } + ], + "id": 3756464, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 4 Personen", + "en": "Compartment for 4 passengers", + "fr": ".", + "it": "scompartimento per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 70, + "capacity": 4 + }, + { + "externalIdentifier": "couchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 99.9, + "reservability": "RP" + } + ], + "id": 3756468, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 6 Personen", + "en": "Compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 55, + "capacity": 6 + }, + { + "externalIdentifier": "singlePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 349.9, + "reservability": "RP" + } + ], + "id": 3756876, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", + "en": "Compartment for 1 passenger with shower/WC (Single plus)", + "fr": ".", + "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" + }, + "spotLocations": [], + "surchargePrice": 305, + "capacity": 1 + }, + { + "externalIdentifier": "privateCouchette", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 494.9 + } + ] + } + ], + "surchargePrice": 450 + } + ], + "id": 5000057, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Liegewagen für bis zu 6 Personen", + "en": "Private compartment for up to 6 passengers in a couchette coach", + "fr": ".", + "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "privateSeat", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 224.9 + } + ] + } + ], + "surchargePrice": 180 + } + ], + "id": 5000059, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil im Sitzwagen 2. Klasse", + "en": "Private compartment in a 2nd class seated coach", + "fr": ".", + "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" + }, + "spotLocations": [], + "capacity": 6, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "femaleCouchette4", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 114.9, + "reservability": "RP" + } + ], + "id": 5000068, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 4 Personen", + "en": "Ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 70, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette6", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 99.9, + "reservability": "RP" + } + ], + "id": 5000069, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 6 Personen", + "en": "Ladies only compartment for 6 passengers", + "fr": ".", + "it": "Scompartimento donna per 6 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente finestrinopreferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + } + ], + "surchargePrice": 55, + "capacity": 6 + }, + { + "externalIdentifier": "sideCorridorCoach_2", + "freeFollowupReservationApplied": false, + "accommodationType": "SE", + "special": false, + "objects": [ + { + "index": 0, + "price": 44.9, + "reservability": "RP" + } + ], + "id": 5000075, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Sitzplatz 2. Klasse", + "en": "Seat 2nd class", + "fr": ".", + "it": "Posto a sedere in 2a classe" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "möglichst am Fenster", + "en": "if possible at the window", + "fr": "möglichst am Fenster", + "it": "preferibilmente finestrino" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "möglichst in der Mitte", + "en": "if possible in the center", + "fr": "möglichst in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "möglichst am Gang", + "en": "if possible at the corridor", + "fr": "möglichst am Gang", + "it": "preferibilmente corridoio" + } + }, + { + "param": "NECESSARILY_WINDOW_LOWER", + "name": { + "de": "unbedingt am Fenster", + "en": "absolutely situated at the window", + "fr": "unbedingt am Fenster", + "it": "assolutamente finestrino" + } + } + ], + "capacity": 6 + }, + { + "externalIdentifier": "privateCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "privateVariations": [ + { + "count": 1, + "allocations": [ + { + "objects": [ + { + "index": 0, + "price": 494.9 + } + ] + } + ], + "surchargePrice": 450 + } + ], + "id": 5000085, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", + "en": "Comfort private compartment in a couchette coach for up to 4 passengers", + "fr": ".", + "it": "Scompartimento privato comfort per 4 persone al massimo" + }, + "spotLocations": [], + "capacity": 4, + "privateCompartmentContainsSubcompartments": false + }, + { + "externalIdentifier": "couchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 114.9, + "reservability": "RP" + } + ], + "id": 5000107, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil comfort für 4 Personen", + "en": "Comfort compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 70, + "capacity": 4 + }, + { + "externalIdentifier": "femaleCouchette4comfort", + "freeFollowupReservationApplied": false, + "accommodationType": "LE", + "special": false, + "objects": [ + { + "index": 0, + "price": 114.9, + "reservability": "RP" + } + ], + "id": 5000108, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil comfort für 4 Personen", + "en": "Comfort ladies only compartment for 4 passengers", + "fr": ".", + "it": "Scompartimento donna comfort per 4 persone" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 70, + "capacity": 4 + }, + { + "externalIdentifier": "femaleT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 139.9, + "reservability": "RP" + } + ], + "id": 5000151, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 95, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3Plus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 139.9, + "reservability": "RP" + } + ], + "id": 5000153, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", + "en": "Compartment for 3 passengers with shower/WC (Triple plus)", + "fr": ".", + "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 95, + "capacity": 3 + }, + { + "externalIdentifier": "mixedDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 174.9, + "reservability": "RP" + } + ], + "id": 5000160, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 130, + "capacity": 2 + }, + { + "externalIdentifier": "femaleDoublePlus", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 174.9, + "reservability": "RP" + } + ], + "id": 5000162, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", + "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", + "fr": ".", + "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 130, + "capacity": 2 + }, + { + "externalIdentifier": "single", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 334.9, + "reservability": "RP" + } + ], + "id": 5000198, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 1 Person (Single)", + "en": "Compartment for 1 passenger (Single)", + "fr": ".", + "it": "Scompartimento per 1 persona (Single)" + }, + "spotLocations": [], + "surchargePrice": 290, + "capacity": 1 + }, + { + "externalIdentifier": "femaleDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 169.9, + "reservability": "RP" + } + ], + "id": 5000200, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 2 Personen (Double)", + "en": "Ladies only compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento donna per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 125, + "capacity": 2 + }, + { + "externalIdentifier": "mixedDouble", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 169.9, + "reservability": "RP" + } + ], + "id": 5000201, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 2 Personen (Double)", + "en": "Compartment for 2 passengers (Double)", + "fr": ".", + "it": "Scompartimento per 2 persone (Double)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 125, + "capacity": 2 + }, + { + "externalIdentifier": "femaleT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 5000202, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Damenabteil für 3 Personen (Triple)", + "en": "Ladies only compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento donna per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 110, + "capacity": 3 + }, + { + "externalIdentifier": "mixedT3", + "freeFollowupReservationApplied": false, + "accommodationType": "BE", + "special": false, + "objects": [ + { + "index": 0, + "price": 154.9, + "reservability": "RP" + } + ], + "id": 5000204, + "accommodationClass": [ + "class2" + ], + "name": { + "de": "Abteil für 3 Personen (Triple)", + "en": "Compartment for 3 passengers (Triple)", + "fr": ".", + "it": "Scompartimento per 3 persone (Triple)" + }, + "spotLocations": [ + { + "param": "WINDOW_UPPER", + "name": { + "de": "oben", + "en": "top", + "fr": "oben", + "it": "preferibilmente in alto" + } + }, + { + "param": "MIDDLE", + "name": { + "de": "in der Mitte", + "en": "middle", + "fr": "in der Mitte", + "it": "preferibilmente centrale" + } + }, + { + "param": "AISLE_LOWER", + "name": { + "de": "unten", + "en": "lower", + "fr": "unten", + "it": "preferibilmente in basso" + } + } + ], + "surchargePrice": 110, + "capacity": 3 + } + ], + "reservability": "RP" + } + ], + "priceClass2": 0 + }, + "priceClass2": 44.9, + "refCards": [], + "co2Savings": 252.89, + "outputTypes": [ + "MOBILE", + "PDF", + "SECURITY_PAPER" + ], + "prodGroupLabels": [ + "Kein Storno", + "Zugbindung", + "eineFahrt", + "featured", + "nightjetSparschiene" + ], + "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", + "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", + "validityType": "oneway", + "rideBound": true, + "singleVariety": true, + "reservationMandatory": true + } + ], + "informationMessages": [ + { + "category": 99, + "validFrom": "2025-10-15T09:38:00+02:00", + "validTo": "2025-10-14T19:08:00+02:00", + "sectionIndex": 0, + "header": "Please note – timetable subject to change", + "text": "
Due to construction work or other factors affecting our services, the departure and/or arrival times of this service may change even on the selected day. If you buy a ticket, we will send you an email or sms text message to notify you about any modifications. (Please also check your spam folder regularly).
If the departure time changes 60 minutes or more, a refund is also possible and free of charge for a Sparschiene saver ticket up to 15 days before the first day of validity.
", + "textPlain": "Due to construction work or other factors affecting our services, the departure and/or arrival times of this service may change even on the selected day. If you buy a ticket, we will send you an email or sms text message to notify you about any modifications. (Please also check your spam folder regularly).If the departure time changes 60 minutes or more, a refund is also possible and free of charge for a Sparschiene saver ticket up to 15 days before the first day of validity." + } + ], + "njIndex": 0 + } + ], + "filter": { + "maxTrainChanges": 0 + }, + "rfpIndex": 0 + } + ], + "ok": true, + "responseCode": 200 +} +``` + +## Plan + +- send get request to `init/start` -> save x-token +- set get request to `` diff --git a/docs/API.md b/docs/API.md deleted file mode 100644 index 4eaabc7..0000000 --- a/docs/API.md +++ /dev/null @@ -1,3549 +0,0 @@ -# Nightjet API - -Some notes on the nightjet api - -Most from: `https://martinlangbecker.github.io/night-train-apis/?urls.primaryName=NightJet#/Initialize/post_init_start` - -`curl -X 'POST' 'https://www.nightjet.com/nj-booking-ocp/init/start' -H 'accept: application/json' -H 'Referer: https://www.nightjet.com/' -H 'Content-Type: application/json' -d '{ "lang": "de" }' > resp/init_resp.json` - -- start with init request to grab "token" key from response -- the the token (jwt) in connection search as 'x-token' header - -`eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1MzcwMjUsInB1YmxpY0lkIjoiYWZiZjM3NzBjZGU2NDllNTg2ZGJiMWYyOTIxN2NkYWUifQ.FqZMBeORf1SvXzHBOgwvPoTi7xPhbq4tbIg8KMSX5eI` - -## connection request - -WORKING request - -```bash -curl 'https://www.nightjet.com/nj-booking-ocp/connection/8096003/8796001/2025-10-14' \ - -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0' \ - -H 'Accept: application/json' \ - -H 'Accept-Language: en-US,en;q=0.5' \ - -H 'Accept-Encoding: gzip, deflate, br, zstd' \ - -H 'Referer: https://www.nightjet.com/en/ticket-buchen/' \ - -H 'x-token: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1NDEwODUsInB1YmxpY0lkIjoiN2JjYTBhMGZlNjIyNDVlNGIyZWVlYjQzN2NkM2RjMzgifQ._1VxBxOcxQagYg7DpX-gQprU7i6GT7CBOb8SHixJf6I' \ - -H 'Connection: keep-alive' \ - -H 'Cookie: 34d5d3b7c2b81811ce5d8c490a20f30f=2c79d5b872ea8e0ebceca7def7e259d3; NSC_ESNS=21cebc26-33db-189a-9678-daf5b9e7e029_1676131999_2540214590_00000000000566328293' \ - -H 'Sec-Fetch-Dest: empty' \ - -H 'Sec-Fetch-Mode: cors' \ - -H 'Sec-Fetch-Site: same-origin' \ - -H 'DNT: 1' \ - -H 'Sec-GPC: 1' \ - -H 'Priority: u=4' \ - -H 'Pragma: no-cache' \ - -H 'Cache-Control: no-cache'``` - -### connection response - -```json -{ - "connections": [ - { - "from": { - "name": "Berlin Hbf", - "number": "8011160" - }, - "to": { - "name": "Paris Est", - "number": "8700011" - }, - "trains": [ - { - "train": "NJ 40424", - "departure": { - "utc": 1760461680000, - "local": "2025-10-14T19:08:00" - }, - "arrival": { - "utc": 1760513880000, - "local": "2025-10-15T09:38:00" - }, - "trainType": "regular", - "seatAsIC": false - } - ] - }, - { - "from": { - "name": "Berlin Hbf", - "number": "8011160" - }, - "to": { - "name": "Paris Est", - "number": "8700011" - }, - "trains": [ - { - "train": "NJ 40424", - "departure": { - "utc": 1760634480000, - "local": "2025-10-16T19:08:00" - }, - "arrival": { - "utc": 1760686680000, - "local": "2025-10-17T09:38:00" - }, - "trainType": "regular", - "seatAsIC": false - } - ] - }, - { - "from": { - "name": "Berlin Hbf", - "number": "8011160" - }, - "to": { - "name": "Paris Est", - "number": "8700011" - }, - "trains": [ - { - "train": "NJ 40424", - "departure": { - "utc": 1760893680000, - "local": "2025-10-19T19:08:00" - }, - "arrival": { - "utc": 1760945880000, - "local": "2025-10-20T09:38:00" - }, - "trainType": "regular", - "seatAsIC": false - } - ] - } - ] -} -``` - -## booking Request - -example request body - -```json -{ - "njFrom": 8011160, - //"njArr": 1697307840000, - "njDep": 1760461680000, - "njTo": 8700011, - //"finalFrom": 8000199, - //"finalTo": 5696001, - "maxChanges": 0, - "filter": { - // "njTrain": "NJ 40491", - "njDeparture": 1760461680000, - // "njRoute": [ - // { - // "arrival": "2024-06-20T20:21:00+02:00", - // "arrivalAccessExitAmendable": true, - // "arrivalPlatform": "3", - // "departure": "2024-06-20T20:21:00+02:00", - // "departureAccessExitAmendable": true, - // "departurePlatform": "3", - // "departurePlatformRT": "3", - // "gpsCoordinates": [ - // 53455910, - // 9991699 - // ], - // "name": "Hamburg-Harburg", - // "stationNumber": 8000147, - // "stopState": null - // } - // ], - // "njProduct": "ARES Sparschiene Nachtverkehr", - // "privateVariationCount": null - }, - "objects": [ - { - "birthDate": "1993-10-13", - "cards": [ - 127 - ], - "gender": "female", - "type": "person" - } - ], - "relations": [ - { - "lhs": 1, - "relationType": "attendant_of", - "rhs": 0 - } - ], - "lang": "de" -} -``` - -WORKING request body: - -```bash -curl 'https://www.nightjet.com/nj-booking-ocp/offer/get' \ - --compressed \ - -X POST \ - -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0' \ - -H 'Accept: application/json' \ - -H 'Accept-Language: en-US,en;q=0.5' \ - -H 'Accept-Encoding: gzip, deflate, br, zstd' \ - -H 'Referer: https://www.nightjet.com/en/ticket-buchen/' \ - -H 'content-type: application/json' \ - -H 'x-token: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3NTU1NDEwODUsInB1YmxpY0lkIjoiN2JjYTBhMGZlNjIyNDVlNGIyZWVlYjQzN2NkM2RjMzgifQ._1VxBxOcxQagYg7DpX-gQprU7i6GT7CBOb8SHixJf6I' \ - -H 'Origin: https://www.nightjet.com' \ - -H 'Connection: keep-alive' \ - -H 'Cookie: 34d5d3b7c2b81811ce5d8c490a20f30f=2c79d5b872ea8e0ebceca7def7e259d3; NSC_ESNS=21b97221-349f-189a-9678-daf5b9e7e029_1525529053_2925697282_00000000013450580068' \ - -H 'Sec-Fetch-Dest: empty' \ - -H 'Sec-Fetch-Mode: cors' \ - -H 'Sec-Fetch-Site: same-origin' \ - -H 'DNT: 1' \ - -H 'Sec-GPC: 1' \ - -H 'Priority: u=4' \ - --data-raw '{"njFrom":8011160,"njDep":1760461680000,"njTo":8700011,"maxChanges":0,"connections":1,"filter":{"njTrain":"NJ 40424","njDeparture":1760461680000},"objects":[{"type":"person","birthDate":"1995-08-11","cards":[]}],"relations":[],"lang":"en"}' -``` - -request data - -```json -{ - "njFrom": 8011160, - "njDep": 1760461680000, - "njTo": 8700011, - "maxChanges": 0, - "connections": 1, - "filter": { - "njTrain": "NJ 40424", - "njDeparture": 1760461680000 - }, - "objects": [ - { - "type": "person", - "birthDate": "1995-08-11", - "cards": [] - } - ], - "relations": [], - "lang": "en" -} -``` - -### booking response - -```json -{ - "result": [ - { - "connections": [ - { - "sections": [ - { - "departureTimestamp": "2025-10-14T19:08:00+02:00", - "arrivalTimestamp": "2025-10-15T09:38:00+02:00", - "passlist": [ - { - "name": "Berlin Hbf", - "stationNumber": 8011160, - "departure": "2025-10-14T19:08:00+02:00", - "departureAccessExitAmendable": true, - "departurePlatform": "13", - "gpsCoordinates": [ - 52525589, - 13369549 - ] - }, - { - "name": "Göttingen", - "stationNumber": 8000128, - "departure": "2025-10-14T21:37:00+02:00", - "arrival": "2025-10-14T21:35:00+02:00", - "arrivalAccessExitAmendable": true, - "arrivalPlatform": "10", - "departureAccessExitAmendable": true, - "departurePlatform": "10", - "gpsCoordinates": [ - 51536812, - 9926069 - ] - }, - { - "name": "Kassel-Wilhelmshöhe", - "stationNumber": 8003200, - "departure": "2025-10-14T22:08:00+02:00", - "arrival": "2025-10-14T21:58:00+02:00", - "arrivalAccessExitAmendable": true, - "arrivalPlatform": "1", - "departureAccessExitAmendable": true, - "departurePlatform": "1", - "gpsCoordinates": [ - 51312558, - 9447114 - ] - }, - { - "name": "Frankfurt(Main)Süd", - "stationNumber": 8002041, - "departure": "2025-10-15T00:26:00+02:00", - "arrival": "2025-10-15T00:22:00+02:00", - "arrivalAccessExitAmendable": true, - "arrivalPlatform": "6", - "departureAccessExitAmendable": true, - "departurePlatform": "6", - "gpsCoordinates": [ - 50099365, - 8686456 - ] - }, - { - "name": "Mannheim Hbf", - "stationNumber": 8000244, - "departure": "2025-10-15T03:40:00+02:00", - "arrival": "2025-10-15T01:34:00+02:00", - "arrivalAccessExitAmendable": false, - "departureAccessExitAmendable": false, - "gpsCoordinates": [ - 49479352, - 8468917 - ] - }, - { - "name": "Forbach Frontière de l'État", - "stationNumber": 8002021, - "departure": "2025-10-15T05:11:00+02:00", - "arrival": "2025-10-15T05:11:00+02:00", - "arrivalAccessExitAmendable": false, - "departureAccessExitAmendable": false, - "gpsCoordinates": [ - 49214089, - 6944311 - ] - }, - { - "name": "Paris Est", - "stationNumber": 8700011, - "arrival": "2025-10-15T09:38:00+02:00", - "arrivalAccessExitAmendable": true, - "stopState": [ - "ARR_PROGNOSED" - ], - "gpsCoordinates": [ - 48876976, - 2359120 - ] - } - ], - "attributes": [ - { - "attributeType": "operator", - "range": [ - 0, - 6 - ], - "value": "DPN", - "svalue": "DPN", - "lvalue": "Nahreisezug" - }, - { - "attributeType": "operator_id", - "range": [ - 0, - 6 - ], - "value": "81" - }, - { - "attributeType": "pclass", - "range": [ - 0, - 6 - ], - "value": 3 - }, - { - "attributeType": "intcat", - "range": [ - 0, - 6 - ], - "value": "NJ" - }, - { - "attributeType": "cat", - "range": [ - 0, - 6 - ], - "value": "NJ ", - "svalue": "NJ", - "lvalue": "nightjet", - "code": "3" - }, - { - "attributeType": "name", - "range": [ - 0, - 6 - ], - "value": "NJ 40424" - }, - { - "attributeType": "num", - "range": [ - 0, - 6 - ], - "value": "40424" - }, - { - "attributeType": "intnum", - "range": [ - 0, - 6 - ], - "value": 40424 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Subject to compulsory reservation", - "code": "RP", - "priority": 1 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "space for wheelchairs", - "code": "RO", - "priority": 150 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Wheelchair space - For advance notification, call +43 5 1717", - "code": "OA", - "priority": 150 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "WC accessible for wheelchair", - "code": "OC", - "priority": 150 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Bicycles conveyed - subject to reservation", - "code": "FR", - "priority": 250 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Number of bicycles conveyed limited", - "code": "FK", - "priority": 250 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "2nd class only seated accommodation", - "code": "J2", - "priority": 300 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "ÖBB Nightjet (www.nightjet.com)", - "code": "OJ", - "priority": 320 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "IRT Integrated Reservation Ticket", - "code": "CT", - "priority": 320 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Global price", - "code": "GP", - "priority": 320 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Sleeping-car", - "code": "SW", - "priority": 400 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Couchettes", - "code": "LW", - "priority": 400 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "snacks and beverages available from sleeper/couchette attendant", - "code": "MN", - "priority": 450 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Timetable is subject to change or adjustment", - "code": "50", - "priority": 921 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "Hinweis: Fahrzeiten können sich noch ändern.", - "code": "s1", - "priority": 921 - }, - { - "attributeType": "generic", - "range": [ - 0, - 6 - ], - "value": "nightjet", - "code": "ZN", - "priority": 100 - }, - { - "attributeType": "info", - "range": [ - 0, - 6 - ], - "value": "Paris Est", - "code": "RL" - }, - { - "attributeType": "dir", - "range": [ - 0, - 6 - ], - "value": "Paris Est" - } - ], - "i": 0, - "transportType": "journey" - } - ], - "offers": [ - { - "name": "Standard-Ticket inkl. Reservierung", - "productType": "CONNECTION", - "partialOffer": false, - "productDetails": [ - { - "scope": [ - [ - 0, - 0, - 0, - 6 - ] - ], - "title": "ARES Standard-Ticket Nachtverkehr", - "name": { - "de": "Standard-Ticket inkl. Reservierung", - "en": "Standard-Ticket incl. Reservation", - "it": "Standard-Ticket prenotazione incl." - }, - "validityPeriodFrom": "2025-10-14T19:08:00+02:00", - "validityPeriodTo": "2025-10-15T09:38:00+02:00", - "globallyPriced": true, - "objects": [ - { - "index": 0, - "priceClass2": 89.9 - } - ], - "prodGroupLabels": [ - "918 1 Reservierung Default", - "Vollstorno", - "Zugbindung", - "eineFahrt", - "featured" - ], - "validityType": "oneway" - } - ], - "reservation": { - "reservationSegments": [ - { - "scope": [ - 0, - 0, - 0, - 6 - ], - "compartments": [ - { - "externalIdentifier": "couchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 3756464, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 4 Personen", - "en": "Compartment for 4 passengers", - "fr": ".", - "it": "scompartimento per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "couchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 134.9, - "reservability": "RP" - } - ], - "id": 3756468, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 6 Personen", - "en": "Compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 45, - "capacity": 6 - }, - { - "externalIdentifier": "singlePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 664.9, - "reservability": "RP" - } - ], - "id": 3756876, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", - "en": "Compartment for 1 passenger with shower/WC (Single plus)", - "fr": ".", - "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" - }, - "spotLocations": [], - "surchargePrice": 575, - "capacity": 1 - }, - { - "externalIdentifier": "privateCouchette", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 564.9 - } - ] - } - ], - "surchargePrice": 475 - } - ], - "id": 5000057, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Liegewagen für bis zu 6 Personen", - "en": "Private compartment for up to 6 passengers in a couchette coach", - "fr": ".", - "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "privateSeat", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 424.9 - } - ] - } - ], - "surchargePrice": 335 - } - ], - "id": 5000059, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Sitzwagen 2. Klasse", - "en": "Private compartment in a 2nd class seated coach", - "fr": ".", - "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "femaleCouchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 5000068, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 4 Personen", - "en": "Ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 134.9, - "reservability": "RP" - } - ], - "id": 5000069, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 6 Personen", - "en": "Ladies only compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento donna per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - } - ], - "surchargePrice": 45, - "capacity": 6 - }, - { - "externalIdentifier": "sideCorridorCoach_2", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "objects": [ - { - "index": 0, - "price": 89.9, - "reservability": "RP" - } - ], - "id": 5000075, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Sitzplatz 2. Klasse", - "en": "Seat 2nd class", - "fr": ".", - "it": "Posto a sedere in 2a classe" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "möglichst am Fenster", - "en": "if possible at the window", - "fr": "möglichst am Fenster", - "it": "preferibilmente finestrino" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "möglichst in der Mitte", - "en": "if possible in the center", - "fr": "möglichst in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "möglichst am Gang", - "en": "if possible at the corridor", - "fr": "möglichst am Gang", - "it": "preferibilmente corridoio" - } - }, - { - "param": "NECESSARILY_WINDOW_LOWER", - "name": { - "de": "unbedingt am Fenster", - "en": "absolutely situated at the window", - "fr": "unbedingt am Fenster", - "it": "assolutamente finestrino" - } - } - ], - "capacity": 6 - }, - { - "externalIdentifier": "privateCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 564.9 - } - ] - } - ], - "surchargePrice": 475 - } - ], - "id": 5000085, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", - "en": "Comfort private compartment in a couchette coach for up to 4 passengers", - "fr": ".", - "it": "Scompartimento privato comfort per 4 persone al massimo" - }, - "spotLocations": [], - "capacity": 4, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "couchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 5000107, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil comfort für 4 Personen", - "en": "Comfort compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 5000108, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil comfort für 4 Personen", - "en": "Comfort ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 264.9, - "reservability": "RP" - } - ], - "id": 5000151, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 175, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 264.9, - "reservability": "RP" - } - ], - "id": 5000153, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 175, - "capacity": 3 - }, - { - "externalIdentifier": "mixedDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 329.9, - "reservability": "RP" - } - ], - "id": 5000160, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 240, - "capacity": 2 - }, - { - "externalIdentifier": "femaleDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 329.9, - "reservability": "RP" - } - ], - "id": 5000162, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 240, - "capacity": 2 - }, - { - "externalIdentifier": "single", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 514.9, - "reservability": "RP" - } - ], - "id": 5000198, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person (Single)", - "en": "Compartment for 1 passenger (Single)", - "fr": ".", - "it": "Scompartimento per 1 persona (Single)" - }, - "spotLocations": [], - "surchargePrice": 425, - "capacity": 1 - }, - { - "externalIdentifier": "femaleDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 259.9, - "reservability": "RP" - } - ], - "id": 5000200, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen (Double)", - "en": "Ladies only compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento donna per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 170, - "capacity": 2 - }, - { - "externalIdentifier": "mixedDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 259.9, - "reservability": "RP" - } - ], - "id": 5000201, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen (Double)", - "en": "Compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 170, - "capacity": 2 - }, - { - "externalIdentifier": "femaleT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 204.9, - "reservability": "RP" - } - ], - "id": 5000202, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen (Triple)", - "en": "Ladies only compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento donna per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 115, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 204.9, - "reservability": "RP" - } - ], - "id": 5000204, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen (Triple)", - "en": "Compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 115, - "capacity": 3 - } - ], - "reservability": "RP" - } - ], - "priceClass2": 0 - }, - "priceClass2": 89.9, - "refCards": [], - "co2Savings": 252.89, - "outputTypes": [ - "MOBILE", - "PDF", - "SECURITY_PAPER" - ], - "prodGroupLabels": [ - "918 1 Reservierung Default", - "Vollstorno", - "Zugbindung", - "eineFahrt", - "featured" - ], - "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", - "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", - "validityType": "oneway", - "rideBound": true, - "singleVariety": true, - "reservationMandatory": true - }, - { - "name": "Sparschiene Komfort inkl. Reservierung", - "productType": "CONNECTION", - "partialOffer": false, - "productDetails": [ - { - "scope": [ - [ - 0, - 0, - 0, - 6 - ] - ], - "title": "ARES Sparschiene Komfort Nachtverkehr", - "name": { - "de": "Sparschiene Komfort inkl. Reservierung", - "en": "Sparschiene Komfort incl. Reservation", - "it": "Sparschiene Komfort prenotazione incl." - }, - "validityPeriodFrom": "2025-10-14T19:08:00+02:00", - "validityPeriodTo": "2025-10-15T09:38:00+02:00", - "globallyPriced": true, - "objects": [ - { - "index": 0, - "priceClass2": 64.9 - } - ], - "prodGroupLabels": [ - "Zugbindung", - "eineFahrt", - "featured", - "komfortticketStorno" - ], - "validityType": "oneway" - } - ], - "reservation": { - "reservationSegments": [ - { - "scope": [ - 0, - 0, - 0, - 6 - ], - "compartments": [ - { - "externalIdentifier": "couchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 129.9, - "reservability": "RP" - } - ], - "id": 3756464, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 4 Personen", - "en": "Compartment for 4 passengers", - "fr": ".", - "it": "scompartimento per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "couchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 109.9, - "reservability": "RP" - } - ], - "id": 3756468, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 6 Personen", - "en": "Compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 45, - "capacity": 6 - }, - { - "externalIdentifier": "singlePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 494.9, - "reservability": "RP" - } - ], - "id": 3756876, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", - "en": "Compartment for 1 passenger with shower/WC (Single plus)", - "fr": ".", - "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" - }, - "spotLocations": [], - "surchargePrice": 430, - "capacity": 1 - }, - { - "externalIdentifier": "privateCouchette", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 519.9 - } - ] - } - ], - "surchargePrice": 455 - } - ], - "id": 5000057, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Liegewagen für bis zu 6 Personen", - "en": "Private compartment for up to 6 passengers in a couchette coach", - "fr": ".", - "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "privateSeat", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 319.9 - } - ] - } - ], - "surchargePrice": 255 - } - ], - "id": 5000059, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Sitzwagen 2. Klasse", - "en": "Private compartment in a 2nd class seated coach", - "fr": ".", - "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "femaleCouchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 129.9, - "reservability": "RP" - } - ], - "id": 5000068, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 4 Personen", - "en": "Ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 109.9, - "reservability": "RP" - } - ], - "id": 5000069, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 6 Personen", - "en": "Ladies only compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento donna per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - } - ], - "surchargePrice": 45, - "capacity": 6 - }, - { - "externalIdentifier": "sideCorridorCoach_2", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "objects": [ - { - "index": 0, - "price": 64.9, - "reservability": "RP" - } - ], - "id": 5000075, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Sitzplatz 2. Klasse", - "en": "Seat 2nd class", - "fr": ".", - "it": "Posto a sedere in 2a classe" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "möglichst am Fenster", - "en": "if possible at the window", - "fr": "möglichst am Fenster", - "it": "preferibilmente finestrino" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "möglichst in der Mitte", - "en": "if possible in the center", - "fr": "möglichst in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "möglichst am Gang", - "en": "if possible at the corridor", - "fr": "möglichst am Gang", - "it": "preferibilmente corridoio" - } - }, - { - "param": "NECESSARILY_WINDOW_LOWER", - "name": { - "de": "unbedingt am Fenster", - "en": "absolutely situated at the window", - "fr": "unbedingt am Fenster", - "it": "assolutamente finestrino" - } - } - ], - "capacity": 6 - }, - { - "externalIdentifier": "privateCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 519.9 - } - ] - } - ], - "surchargePrice": 455 - } - ], - "id": 5000085, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", - "en": "Comfort private compartment in a couchette coach for up to 4 passengers", - "fr": ".", - "it": "Scompartimento privato comfort per 4 persone al massimo" - }, - "spotLocations": [], - "capacity": 4, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "couchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 129.9, - "reservability": "RP" - } - ], - "id": 5000107, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil comfort für 4 Personen", - "en": "Comfort compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 129.9, - "reservability": "RP" - } - ], - "id": 5000108, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil comfort für 4 Personen", - "en": "Comfort ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 65, - "capacity": 4 - }, - { - "externalIdentifier": "femaleT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 199.9, - "reservability": "RP" - } - ], - "id": 5000151, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 135, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 199.9, - "reservability": "RP" - } - ], - "id": 5000153, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 135, - "capacity": 3 - }, - { - "externalIdentifier": "mixedDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 249.9, - "reservability": "RP" - } - ], - "id": 5000160, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 185, - "capacity": 2 - }, - { - "externalIdentifier": "femaleDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 249.9, - "reservability": "RP" - } - ], - "id": 5000162, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 185, - "capacity": 2 - }, - { - "externalIdentifier": "single", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 384.9, - "reservability": "RP" - } - ], - "id": 5000198, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person (Single)", - "en": "Compartment for 1 passenger (Single)", - "fr": ".", - "it": "Scompartimento per 1 persona (Single)" - }, - "spotLocations": [], - "surchargePrice": 320, - "capacity": 1 - }, - { - "externalIdentifier": "femaleDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 194.9, - "reservability": "RP" - } - ], - "id": 5000200, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen (Double)", - "en": "Ladies only compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento donna per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 130, - "capacity": 2 - }, - { - "externalIdentifier": "mixedDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 194.9, - "reservability": "RP" - } - ], - "id": 5000201, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen (Double)", - "en": "Compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 130, - "capacity": 2 - }, - { - "externalIdentifier": "femaleT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 169.9, - "reservability": "RP" - } - ], - "id": 5000202, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen (Triple)", - "en": "Ladies only compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento donna per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 105, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 169.9, - "reservability": "RP" - } - ], - "id": 5000204, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen (Triple)", - "en": "Compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 105, - "capacity": 3 - } - ], - "reservability": "RP" - } - ], - "priceClass2": 0 - }, - "priceClass2": 64.9, - "refCards": [], - "co2Savings": 252.89, - "outputTypes": [ - "MOBILE", - "PDF", - "SECURITY_PAPER" - ], - "prodGroupLabels": [ - "Zugbindung", - "eineFahrt", - "featured", - "komfortticketStorno" - ], - "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", - "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", - "validityType": "oneway", - "rideBound": true, - "singleVariety": true, - "reservationMandatory": true - }, - { - "name": "Sparschiene inkl. Reservierung", - "productType": "CONNECTION", - "partialOffer": false, - "productDetails": [ - { - "scope": [ - [ - 0, - 0, - 0, - 6 - ] - ], - "title": "ARES Sparschiene Nachtverkehr", - "name": { - "de": "Sparschiene inkl. Reservierung", - "en": "Sparschiene incl. Reservation", - "it": "Sparschiene prenotazione incl." - }, - "validityPeriodFrom": "2025-10-14T19:08:00+02:00", - "validityPeriodTo": "2025-10-15T09:38:00+02:00", - "globallyPriced": true, - "objects": [ - { - "index": 0, - "priceClass2": 44.9 - } - ], - "prodGroupLabels": [ - "Kein Storno", - "Zugbindung", - "eineFahrt", - "featured", - "nightjetSparschiene" - ], - "validityType": "oneway" - } - ], - "reservation": { - "reservationSegments": [ - { - "scope": [ - 0, - 0, - 0, - 6 - ], - "compartments": [ - { - "externalIdentifier": "couchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 114.9, - "reservability": "RP" - } - ], - "id": 3756464, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 4 Personen", - "en": "Compartment for 4 passengers", - "fr": ".", - "it": "scompartimento per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 70, - "capacity": 4 - }, - { - "externalIdentifier": "couchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 99.9, - "reservability": "RP" - } - ], - "id": 3756468, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 6 Personen", - "en": "Compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 55, - "capacity": 6 - }, - { - "externalIdentifier": "singlePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 349.9, - "reservability": "RP" - } - ], - "id": 3756876, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person mit Dusche/WC (Single plus)", - "en": "Compartment for 1 passenger with shower/WC (Single plus)", - "fr": ".", - "it": "Scompartimento per 1 person1 con doccia/WC (Single plus)" - }, - "spotLocations": [], - "surchargePrice": 305, - "capacity": 1 - }, - { - "externalIdentifier": "privateCouchette", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 494.9 - } - ] - } - ], - "surchargePrice": 450 - } - ], - "id": 5000057, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Liegewagen für bis zu 6 Personen", - "en": "Private compartment for up to 6 passengers in a couchette coach", - "fr": ".", - "it": "Scompartimento privato in carrozza cuccette per 6 passeggeri al massimo" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "privateSeat", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 224.9 - } - ] - } - ], - "surchargePrice": 180 - } - ], - "id": 5000059, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil im Sitzwagen 2. Klasse", - "en": "Private compartment in a 2nd class seated coach", - "fr": ".", - "it": "Scompartimento privato in carrozza con posti a sedere, 2a classe" - }, - "spotLocations": [], - "capacity": 6, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "femaleCouchette4", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 114.9, - "reservability": "RP" - } - ], - "id": 5000068, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 4 Personen", - "en": "Ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 70, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette6", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 99.9, - "reservability": "RP" - } - ], - "id": 5000069, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 6 Personen", - "en": "Ladies only compartment for 6 passengers", - "fr": ".", - "it": "Scompartimento donna per 6 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente finestrinopreferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - } - ], - "surchargePrice": 55, - "capacity": 6 - }, - { - "externalIdentifier": "sideCorridorCoach_2", - "freeFollowupReservationApplied": false, - "accommodationType": "SE", - "special": false, - "objects": [ - { - "index": 0, - "price": 44.9, - "reservability": "RP" - } - ], - "id": 5000075, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Sitzplatz 2. Klasse", - "en": "Seat 2nd class", - "fr": ".", - "it": "Posto a sedere in 2a classe" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "möglichst am Fenster", - "en": "if possible at the window", - "fr": "möglichst am Fenster", - "it": "preferibilmente finestrino" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "möglichst in der Mitte", - "en": "if possible in the center", - "fr": "möglichst in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "möglichst am Gang", - "en": "if possible at the corridor", - "fr": "möglichst am Gang", - "it": "preferibilmente corridoio" - } - }, - { - "param": "NECESSARILY_WINDOW_LOWER", - "name": { - "de": "unbedingt am Fenster", - "en": "absolutely situated at the window", - "fr": "unbedingt am Fenster", - "it": "assolutamente finestrino" - } - } - ], - "capacity": 6 - }, - { - "externalIdentifier": "privateCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "privateVariations": [ - { - "count": 1, - "allocations": [ - { - "objects": [ - { - "index": 0, - "price": 494.9 - } - ] - } - ], - "surchargePrice": 450 - } - ], - "id": 5000085, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Privatabteil comfort im Liegewagen für bis zu 4 Personen", - "en": "Comfort private compartment in a couchette coach for up to 4 passengers", - "fr": ".", - "it": "Scompartimento privato comfort per 4 persone al massimo" - }, - "spotLocations": [], - "capacity": 4, - "privateCompartmentContainsSubcompartments": false - }, - { - "externalIdentifier": "couchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 114.9, - "reservability": "RP" - } - ], - "id": 5000107, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil comfort für 4 Personen", - "en": "Comfort compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 70, - "capacity": 4 - }, - { - "externalIdentifier": "femaleCouchette4comfort", - "freeFollowupReservationApplied": false, - "accommodationType": "LE", - "special": false, - "objects": [ - { - "index": 0, - "price": 114.9, - "reservability": "RP" - } - ], - "id": 5000108, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil comfort für 4 Personen", - "en": "Comfort ladies only compartment for 4 passengers", - "fr": ".", - "it": "Scompartimento donna comfort per 4 persone" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 70, - "capacity": 4 - }, - { - "externalIdentifier": "femaleT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 139.9, - "reservability": "RP" - } - ], - "id": 5000151, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Ladies only compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento donna per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 95, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3Plus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 139.9, - "reservability": "RP" - } - ], - "id": 5000153, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen mit Dusche/WC (Triple plus)", - "en": "Compartment for 3 passengers with shower/WC (Triple plus)", - "fr": ".", - "it": "Scompartimento per 3 persone con doccia/WC (Triple plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 95, - "capacity": 3 - }, - { - "externalIdentifier": "mixedDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 174.9, - "reservability": "RP" - } - ], - "id": 5000160, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 130, - "capacity": 2 - }, - { - "externalIdentifier": "femaleDoublePlus", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 174.9, - "reservability": "RP" - } - ], - "id": 5000162, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen mit Dusche/WC (Double plus)", - "en": "Ladies only compartment for 2 passengers with shower/WC (Double plus)", - "fr": ".", - "it": "Scompartimento donna per 2 persone con doccia/WC (Double plus)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 130, - "capacity": 2 - }, - { - "externalIdentifier": "single", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 334.9, - "reservability": "RP" - } - ], - "id": 5000198, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 1 Person (Single)", - "en": "Compartment for 1 passenger (Single)", - "fr": ".", - "it": "Scompartimento per 1 persona (Single)" - }, - "spotLocations": [], - "surchargePrice": 290, - "capacity": 1 - }, - { - "externalIdentifier": "femaleDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 169.9, - "reservability": "RP" - } - ], - "id": 5000200, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 2 Personen (Double)", - "en": "Ladies only compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento donna per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 125, - "capacity": 2 - }, - { - "externalIdentifier": "mixedDouble", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 169.9, - "reservability": "RP" - } - ], - "id": 5000201, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 2 Personen (Double)", - "en": "Compartment for 2 passengers (Double)", - "fr": ".", - "it": "Scompartimento per 2 persone (Double)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 125, - "capacity": 2 - }, - { - "externalIdentifier": "femaleT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 5000202, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Damenabteil für 3 Personen (Triple)", - "en": "Ladies only compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento donna per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 110, - "capacity": 3 - }, - { - "externalIdentifier": "mixedT3", - "freeFollowupReservationApplied": false, - "accommodationType": "BE", - "special": false, - "objects": [ - { - "index": 0, - "price": 154.9, - "reservability": "RP" - } - ], - "id": 5000204, - "accommodationClass": [ - "class2" - ], - "name": { - "de": "Abteil für 3 Personen (Triple)", - "en": "Compartment for 3 passengers (Triple)", - "fr": ".", - "it": "Scompartimento per 3 persone (Triple)" - }, - "spotLocations": [ - { - "param": "WINDOW_UPPER", - "name": { - "de": "oben", - "en": "top", - "fr": "oben", - "it": "preferibilmente in alto" - } - }, - { - "param": "MIDDLE", - "name": { - "de": "in der Mitte", - "en": "middle", - "fr": "in der Mitte", - "it": "preferibilmente centrale" - } - }, - { - "param": "AISLE_LOWER", - "name": { - "de": "unten", - "en": "lower", - "fr": "unten", - "it": "preferibilmente in basso" - } - } - ], - "surchargePrice": 110, - "capacity": 3 - } - ], - "reservability": "RP" - } - ], - "priceClass2": 0 - }, - "priceClass2": 44.9, - "refCards": [], - "co2Savings": 252.89, - "outputTypes": [ - "MOBILE", - "PDF", - "SECURITY_PAPER" - ], - "prodGroupLabels": [ - "Kein Storno", - "Zugbindung", - "eineFahrt", - "featured", - "nightjetSparschiene" - ], - "validityPeriodFrom": "2025-10-14T19:08:00.000+02:00", - "validityPeriodTo": "2025-10-15T09:38:00.000+02:00", - "validityType": "oneway", - "rideBound": true, - "singleVariety": true, - "reservationMandatory": true - } - ], - "informationMessages": [ - { - "category": 99, - "validFrom": "2025-10-15T09:38:00+02:00", - "validTo": "2025-10-14T19:08:00+02:00", - "sectionIndex": 0, - "header": "Please note – timetable subject to change", - "text": "Due to construction work or other factors affecting our services, the departure and/or arrival times of this service may change even on the selected day. If you buy a ticket, we will send you an email or sms text message to notify you about any modifications. (Please also check your spam folder regularly).
If the departure time changes 60 minutes or more, a refund is also possible and free of charge for a Sparschiene saver ticket up to 15 days before the first day of validity.
", - "textPlain": "Due to construction work or other factors affecting our services, the departure and/or arrival times of this service may change even on the selected day. If you buy a ticket, we will send you an email or sms text message to notify you about any modifications. (Please also check your spam folder regularly).If the departure time changes 60 minutes or more, a refund is also possible and free of charge for a Sparschiene saver ticket up to 15 days before the first day of validity." - } - ], - "njIndex": 0 - } - ], - "filter": { - "maxTrainChanges": 0 - }, - "rfpIndex": 0 - } - ], - "ok": true, - "responseCode": 200 -} -``` - diff --git a/src/nj/main.py b/main.py similarity index 62% rename from src/nj/main.py rename to main.py index b1c62c4..c5f406c 100644 --- a/src/nj/main.py +++ b/main.py @@ -3,23 +3,20 @@ import json from dataclasses import dataclass from datetime import datetime from pathlib import Path -from time import sleep -from typing import Annotated, Any +from typing import Any import requests import typer BASE_URL = "https://www.nightjet.com" BASE_DIR = "out" -CSV_LOWEST_FILE = "lowest.csv" -CSV_ALL_PRICES_PATTERN = "all_prices_%%DATE%%.csv" +CSV_LOWEST_FILE = f"{BASE_DIR}/lowest.csv" +CSV_ALL_PRICES_PATTERN = f"{BASE_DIR}/%%DATE%%_all_prices.csv" NOTIFICATION_CHANNEL = "nightjet-price-notifier" START_STATION = "8096003" # BerlinHBF END_STATION = "8796001" # Paris Est -TRAVELLER_BIRTHDATE = datetime(1990, 1, 1) # TODO: could randomize a little - -MONITOR_FREQUENCY = 3600 +TRAVEL_DATE = "2025-10-14" def dprint(txt) -> None: @@ -64,9 +61,10 @@ def request_connections( return resp_json["connections"] -def connection_data_to_booking_requests( - connections, traveller_birthdate: datetime = TRAVELLER_BIRTHDATE -) -> list[dict[str, Any]]: +TRAVELLER_BIRTHDATE = "2000-07-15" # TODO: randomize a little + + +def connection_data_to_booking_requests(connections) -> list[dict[str, Any]]: b_requests = [] for c in connections: train = c["trains"][0] @@ -82,11 +80,7 @@ def connection_data_to_booking_requests( "njDeparture": dep, # departure time again }, "objects": [ # traveller - { - "type": "person", - "birthDate": traveller_birthdate.strftime("%Y-%m-%d"), - "cards": [], - } + {"type": "person", "birthDate": TRAVELLER_BIRTHDATE, "cards": []} ], "relations": [], "lang": "en", @@ -199,35 +193,22 @@ def get_lowest_price(prices: list[Price]) -> Price: return lowest -def dump_all_prices_to_csv(prices: list[Price], fpath: Path) -> None: - fstr = str(fpath) - fpath_replaced = Path( - fstr.replace("%%DATE%%", str(int(datetime.now().timestamp()))) +def dump_all_prices_to_csv(prices: list[Price]) -> None: + fname = CSV_ALL_PRICES_PATTERN.replace( + "%%DATE%%", str(int(datetime.now().timestamp())) ) - with open(fpath_replaced, "w") as f: + with open(fname, "w") as f: writer = csv.writer(f) - writer.writerow(["id", "price", "ts_from", "ts_to", "name"]) - writer.writerows( - [ - [ - price.id, - price.price, - price.dt_from.timestamp(), - price.dt_to.timestamp(), - price.name, - ] - for price in prices - ] - ) - dprint(f"Dumped current query snapshot into: {fpath_replaced}.") + writer.writerow(["id", "price", "name"]) + writer.writerows([[price.id, price.price, price.name] for price in prices]) -def add_to_csv(price: Price, file: Path) -> None: - if not file.is_file(): - with open(file, "w") as f: +def add_to_csv(price: Price) -> None: + if not Path(CSV_LOWEST_FILE).is_file(): + with open(CSV_LOWEST_FILE, "w") as f: csv.writer(f).writerow(["id", "price", "ts_from", "ts_to", "name"]) - with open(file, "a") as f: + with open(CSV_LOWEST_FILE, "a") as f: csv.writer(f).writerow( [ price.id, @@ -239,11 +220,11 @@ def add_to_csv(price: Price, file: Path) -> None: ) -def get_last_price_from_csv(file: Path) -> Price | None: - if not file.is_file(): +def get_last_price_from_csv() -> Price | None: + if not Path(CSV_LOWEST_FILE).is_file(): return - with open(file) as f: + with open(CSV_LOWEST_FILE) as f: last = next(reversed(list(csv.reader(f)))) return Price( id=last[0], @@ -266,14 +247,40 @@ def notify_user(previous: Price, new: Price, channel: str) -> None: ) -def query(start_station: int, end_station: int, travel_date: datetime, traveller_birthdate: datetime) -> list[Price]: +def main(start_station: int, end_station: int, travel_date: datetime): + Path(BASE_DIR).mkdir(exist_ok=True, parents=True) + print(start_station, end_station, travel_date) + # return + token = request_init_token() connections = request_connections(token, start_station, end_station, travel_date) - booking_requests = connection_data_to_booking_requests(connections, traveller_birthdate=traveller_birthdate) + booking_requests = connection_data_to_booking_requests(connections) bookings = [request_bookings(token, req) for req in booking_requests] prices = extract_prices(bookings) - return prices + # create a snapshot of all current prices + dump_all_prices_to_csv(prices) + + # extract the lowest and the last lowest price + new = get_lowest_price(prices) + previous = get_last_price_from_csv() + + # if the price changed, add it to lowest prices + if not previous or new.price != previous.price: + dprint(f"PRICE CHANGE. {previous} -> {new}") + notify_user( + previous + or Price( + "", + "No previous price", + 0.0, + datetime.fromtimestamp(0), + datetime.fromtimestamp(0), + ), + new, + NOTIFICATION_CHANNEL, + ) + add_to_csv(new) ## CLI @@ -281,90 +288,21 @@ app = typer.Typer() @app.command() -def main( - travel_date: Annotated[ - str, typer.Argument(help="Travel day to search from. (YYYY-MM-DD)") - ], +def search( start_station: int = typer.Option( START_STATION, help="Departure station number. (default: Berlin Hbf)" ), end_station: int = typer.Option( END_STATION, help="Destination station number. (default: Paris Est)" ), - birthdate: str = typer.Option(TRAVELLER_BIRTHDATE.strftime("%Y-%m-%d"), help="Traveller birthdate, may be important for discounts. (YYYY-MM-DD)"), - notification_channel: str = typer.Option( - NOTIFICATION_CHANNEL, help="ntfy channel to inform user on." - ), - monitor_mode: bool = typer.Option( - True, - help="Run queries repeatedly over time. If False only runs a single query (oneshot mode).", - ), - monitor_frequency: int = typer.Option( - MONITOR_FREQUENCY, - help="How often to run price queries if in monitoring mode, in seconds.", - ), - base_output_directory: Path = typer.Option( - Path(BASE_DIR), help="Directory in which to output all result files." - ), - lowest_prices_filename: str = typer.Option( - CSV_LOWEST_FILE, help="Filename for collecting lowest found prices." - ), - price_snapshot_pattern: str = typer.Option( - CSV_ALL_PRICES_PATTERN, - help="Filename pattern for saving all prices of each query. Takes %%DATE%% as pattern to replace with current unix timestamp.", - ), - dump_price_snapshot: bool = typer.Option( - True, help="Dump _all_ queried prices into a timestamped csv file." - ), + travel_date: str = typer.Option(help="Travel day to search from. (YYYY-MM-DD)"), ): - base_output_directory.mkdir(exist_ok=True, parents=True) - lowest_prices_path = base_output_directory.joinpath(lowest_prices_filename) - price_snapshot_path = base_output_directory.joinpath(price_snapshot_pattern) - try: - travel_date_obj = datetime.strptime(travel_date, "%Y-%m-%d") - birth_date_obj = datetime.strptime(birthdate, "%Y-%m-%d") + date_obj = datetime.strptime(travel_date, "%Y-%m-%d") except ValueError: - typer.echo("Invalid date format. Use YYYY-MM-DD", err=True) + typer.echo(f"Invalid date format: {travel_date}. Use YYYY-MM-DD", err=True) raise typer.Exit(1) - - while True: - prices = query( - start_station=start_station, end_station=end_station, travel_date=travel_date_obj, traveller_birthdate=birth_date_obj, - ) - - # create a snapshot of all current prices - if dump_price_snapshot: - dump_all_prices_to_csv(prices, price_snapshot_path) - - # extract the lowest and the last lowest price - new = get_lowest_price(prices) - previous = get_last_price_from_csv(lowest_prices_path) - - # if the price changed, add it to lowest prices - if not previous or new.price != previous.price: - dprint(f"PRICE CHANGE. {previous} -> {new}") - add_to_csv(new, lowest_prices_path) - notify_user( - previous - or Price( - "", - "No previous price", - 0.0, - datetime.fromtimestamp(0), - datetime.fromtimestamp(0), - ), - new, - notification_channel, - ) - - # oneshot exit - if not monitor_mode: - break - dprint( - f"Query complete. Monitoring mode active, sleeping for {monitor_frequency} seconds..." - ) - sleep(monitor_frequency) + main(start_station=start_station, end_station=end_station, travel_date=date_obj) if __name__ == "__main__": diff --git a/pyproject.toml b/pyproject.toml index 488ab35..0fc68a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,13 @@ [project] -name = "nightjetter" +name = "nj-api" version = "0.1.0" -description = "Continuously monitors nightjet travel dates for price fluctuations" +description = "Add your description here" readme = "README.md" -authors = [{ name = "Marty Oehme", email = "contact@martyoeh.me" }] requires-python = ">=3.13" -dependencies = ["requests>=2.32.4", "typer>=0.16.0"] - -[project.scripts] -nightjet = "nj:main" - -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" +dependencies = [ + "requests>=2.32.4", + "typer>=0.16.0", +] [tool.pyright] typeCheckingMode = "basic" - -[tool.hatch.build.targets.wheel] -packages = ["src/nj"] diff --git a/src/nj/__init__.py b/src/nj/__init__.py deleted file mode 100644 index ab66342..0000000 --- a/src/nj/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from nj import main as cli - - -def main() -> None: - cli.app() diff --git a/uv.lock b/uv.lock index 5ffcdf0..e929183 100644 --- a/uv.lock +++ b/uv.lock @@ -94,9 +94,9 @@ wheels = [ ] [[package]] -name = "nightjetter" +name = "nj-api" version = "0.1.0" -source = { editable = "." } +source = { virtual = "." } dependencies = [ { name = "requests" }, { name = "typer" },