Guides v1

Deprecation Note

The Offers API v1 is being replaced by the Offers API v2. Deprecation date has been set to 1st of October 2023. Developers of existing clients should start migrating to the new version. Developers of new clients are advised to start out with the v2 API.

Table of Contents

How to use the offers API

(back to Table of contents)

Offers API provides endpoints for generating and retrieving offers for a combination of origin and destination. Offers API typically gathers more information about the departure and sends a request to the Products API, where the offer is generated. In addition Offers API is responsible for caching the offers which Products API produces, these offers are cached for 30 minutes.

Offers API also provides the possibility for consumers to use OTP (the journey planner) directly, and then generate offers at a later stage. The offer respons structure will be identical, and the same offers will be generated for any given trip.

Some products are not directly connected to any given journey, for instance period tickets. We have a separate API which can generate offers for these products.

Find trip-patterns and update offers for trip-pattern

(back to Table of contents)

Intention and usage

Find trip-patterns will send a query to the journey planner and get back possible itineraries between two places. At this point the offers API will be responsible for caching the trip-patterns, but it will not have any information regarding the travellers. For optimization purposes we will immediately begin to generate offers for all the trip-patterns, using one adult as our list of travellers. This enables the consumers to fetch this offer as a starting point for the costumers.

If the list of travellers is anything other than one adult the next part is to update offers for trip-pattern, which will create new offers for the tip-pattern for a specified list of travellers. The new offers will also be cached, and will be connected to the trip-pattern, this ensures that the new offers will be returned from the Fetch trip-pattern and offers API.

Input types

Traveller

This field is used in order to help the consumers select the correct userProfile for any traveller. The most common way to find the correct userProfile is to use the userType Enum defined in Netex. It is also possible to find the correct userProfile using age or userProfileRefs

The userType Enum have the following values:

  • Adult
  • Senior
  • Child
  • Infant
  • Youth
  • Military
  • Student
  • Anyone

Create offers for trip

(back to Table of contents)

Intention and usage

Some of our consumers prefer to use the journey planner directly, and then to use Create offers for trip API to generate offers. In order to do this, the offers API will need information about the trip, tripPatterns, travellers and tagSpec. The resulting offers will be identical as to use Find trip-patterns and update offers for trip-pattern.

Input types

Trip

The trips is a way for us to know the costumers origin and destination with no regard to the different travel journeys.

TripPattern

The tripPatterns is a list of legs constituting a suggested sequence of rides and links for a specific trip. The tripPatterns serve as our basis for generating offers.

Traveller

This field is used in order to help the consumers select the correct userProfile for any traveller. The most common way to find the correct userProfile is to use the userType Enum defined in Netex. It is also possible to find the correct userProfile using age or userProfileRefs

The userType Enum have the following values:

  • Adult
  • Senior
  • Child
  • Infant
  • Youth
  • Military
  • Student
  • Anyone

TagSpec

We have covered TagSpec as a separate chapter further down in this guide.

Using /search/stop-places

(back to Table of contents)

Intention and usage

The search/stop-places endpoint is used to search for offers that are not limited to specific departures such as travel passes and zone based tickets. The endpoint requires only a from- and to-stop-place, and uses those to find operators that offer transport between those stop places by searching for lines that contains both stop places. The endpoint also require a date-time is used to find fare products and prices that are valid for a specific date. Usually the current time should be used as period-tickets are often activated directly after purchase, however any date can be used if the user already knows that the ticket should be activated at that time. Note that it is not relevant for the result whether or not there exists a planned service journey for the given date.

NOTE: Currently the endpoint only works with train-stations

Input types

The SearchStopPlaceRequest allows the user to specify the from- and to-stop-place, the date and a list of travellers. The Traveler-type is described in detail in a separate section above. The request also contains a flag called excludeSingleTripOffers. If true then only offers valid for a day or more are returned.

Create offers without a trip

(back to Table of contents)

Intention and usage

The /search endpoint is meant to make it possible to get an offer for a product without having to specify a trip or any information about the travellers. This is useful for offering period tickets and tickets that dosent require reserving a seat, especially for local travellers that are familiar with the products and the user profiles in the area.

To create an offer using the /search enpoint the client should specify a product-id and the id of the product-parameters that should be used for pricing (refer to the section about offer stucture for a further explanation on product-parameters). If the client has made a choice for every configurable parameter on the product they will get back an offer with those parameters selected.

In some cases it can be useful to let the API choose some of the parameters, for example if the user shouldn't be able to select which user profile they get or if there is an intended trip involved. It is possible through the TravellerGroup and JourneySpec types to specify enough information abouth the trip or the travellers so that the API can select the parameters that are valid. To clearify the difference between this and letting the customer decide which parameters they want consider the following example: A local city product is priced based on how many zones the traveller needs to cross. This is modelled using interval-parameters with different prices attached to them, meaning that in order to get an offer for 3 zones the interval 3 -> 4 zones must be chosen. The different options in the /search enpoint means that the client have two different ways of offering this product to their customers. One would be to fetch all the intervals from the API, present them in a drop-down-menu to the customer and then use the requestedParameters-field to make an offer for the interval that the customer chose. Another approach would be to let the customer select a start- and end-zone and provide those zones to the API using a JourneySpec. In that case the API will find the number of zones in the journey and make an offer with the matching interval.

The client will receive the ids for all parameters that needs to be chosen from a separate endpoint (TBD). If the client lets the customer choose between the product-parameters directly then caching the offers would be a good idea since there often aren't that many possible combinations of parameters. Lets say there is a product that requires a choice between 3 user profiles and 4 zone intervals (all intervals above 4 zones cost the same). Then there are only 12 different offers that can be made so it should be easy to cache them all and create a mapping from each combination of parameters to the matching offer. If the client however opts to let the customer chose start- and end-zone as described earlier there will be many more diferent keys so the cache will be much bigger even though the number of different possible offers is still the same.

Input types

The ProductQuery is the main input parameter of this endpoint. It consists of 3 optional parts

ProductSpec

This is used to specify what/which product(s) you want offers for. To get an offer for a product provide the id of that product in a product spec. To get offers for mulitple products at once create several product specs.

TravellerGroup

This is used to specify information about the travellers. A traveller group consists of a TravellerSpec that describe common properties amongst the travellers in the group an a numberOfTravellers which says how many travellers there are in the group. Its up to the client to create groups that are useful for their needs. For instance for smaller groups it may be sensible do divide the travellers into adults, children, students etc, however for large groups it may be more practical to just create one big group. The caveat is that some products or discounts require specific combinations of travellers (for example a family discount) and if the client havent grouped the travellers accordigly they will not receive offers for those products/discounts. A way to avoid the whole problem of dividing the travellers into groups is to create a separate group for each traveller. Note that this may lead to performance issues if there are many travelers.

JourneySpec

This describes properties of a leg like its start and stop place, the operator for the leg and the line. To describe a trip-pattern consisting of several legs, a list of journey specs can be used.

Requested parameters

The requested parameters field lets the client specify which product-parameters should be used for the offer. It is structured as a map where the keys are strings and the values are lists of strings. The key should be the id of the element in the product-structure that holds the parameters in the choice. The client will receive the ids of the elements that requires a choice along with the ids of all the parameters in the choice from a separate endpoint (TBD). The value should be the id(s) of the parameter(s) that the client/customer wishes to receive an offer for. If several paramer-ids are given there will be created a separate offer for each one. That can be useful if the client wishes to get offers for adults and children simultaneously.

Recommendation

(back to Table of contents)

Introduction

Recommendations is a feature that aims to recommend which offers you need to buy for various situations. Currently we support three kinds of recommendations;

  1. Basic: based on three types of commercial conditions(described in more detail later).
  2. Layered: has two added dimensions, when compared to the basic recommendation. The layered recommendation gives recommendations just like the basic recommendation, but it does it for each combination of service journeys and for each commercial condition there is an extra layer where the clients can specify which fare classes to get recommendations for per commercial condition.
  3. Multidimensional: has three dimensions of which the recommendations are structured. It looks like the layered recommendations, however a added dimension is added for separating different types of accommodation facilities(sleeper, recliner, regular seat, etc.)

Background

Offers produces one offer for each configuration of a PreassigneFareProduct which are relevant for each ride and traveler. An offer can cover multiple travellers, however the amount of offers can still become very high if there are several travellers and multiple operators.

Classification of Products

In order to attack the problem of figuring out which offers to use from the large set of offers, our strategy is to support classification of products. This makes it easier to compare various properties i.e accommodation facilities(sleeper, reclining seat, etc.), conditions(refundability, exchangeability) and price. The criteria for these properties are defined in Netex and such classifications will be consistent across different operators.

Properties in the offer-response used for classification and comparison are:

  • FareClass: FareClassEnum [economy, standard, premium, etc.]
  • AcommodationFacility: AcommodationFacilityEnum [seating, sleeper, familyCompartment, etc]
  • CommercialConditionSummary
    • isRefundable: Boolean
    • isExchangeable: Boolean

Products are categorized as either Flexible, SemiFlexible or NonFlexible, the table below describes how a product is classified.

isRefundableisExchangeable
FlexibleTRUETRUE
SemiFlexibleTRUEFALSE
FALSETRUE
NonFlexibleFALSEFALSE
  • Flexible tickets(full price) allows the traveller to change departure and gives full refund.
  • SemiFlexible tickets are usually a cheaper option, however for these tickets one can either refund or change departure, depending on the commercial condition summary of the product.
  • NonFlexible tickets are usually the cheapest option, but these tickets can't be refunded and the departure can't be changed.

Recommendations of Offer Combinations

When a traveller wants to buy tickets for a trip, the commercial conditions of the ticket are important(wheather or not it can be refunded or exchanged). In order to make it easier for clients to present good suggestions on which offers cover a trip with several rides/legs and travellers, the API will recommend combinations of offers to buy for each category introduced above and in addition, for each service journey combination, for each fare class per category for layered recommendations and in addition for each type of accommodation facility for multidimensional recommendations. The recommendations should be consistent, such that the offers seems identical for the travellers regardless of which client it uses.

In some cases there will be many products that are sold out, and thus we will not be able to find recommendations for each of the commercial conditions. For railway we will always have flexible tickets available until the departure is fully sold out. In such situations it is up to the clients to present available products with various conditions that will cover all the travellers for the entire trip or parts of the trip. In these cases the cheapest recommendation can help present an alternative combination of offers. Depending on the offers generated, one may also need to apply some of the flags to get recommendations on supplement products in combination with preassigned fare products, as some products are made in a way that they are supplement products of a preassigned fare product and even tho the preassigned fare product is sold out, the supplement product with the preassigned fare product is still valid. See more about this in the section about flags

Flags

  • IncludeRecommendedOffersOnly: This flag tells offers to only output a subset of offers, equal to offers contained in the recommendations. All other offers are stripped from the response.
  • IncludeUnavailableOffers: This flag tells the recommendation algorithm to disregard quotas and seating capacities, however it will favor offers combinations which are available.
  • IncludeMultipleUpgrades: This flag lets the recommendation algorithm know that the client supports recommendations which has a combination of supplement products and preassigned fare products. An additional field called "withUpgradeProducts" is added to the response which contains the supplement products needed to buy in addition to the preassigned fare product. Theese supplement products are just represented as strings and multiple entries of the same supplement product means that the client need to buy the same amount of that same supplement product as there are entries of it.
  • SameTicketChange: This flag tells the recommendation algorithm to find combinations of offers where all travellers in the group change tickets on the same service journeys.
  • MixinOffersWithHigherFlexibility: This flag tells the recommendation algorithm that offers with higher degree of flexibility can be mixed in, there can be cases where a flexible offer is cheaper than a semi-flexible or non-flexible offer.
  • IncludeOffersWithMixedCommercialConditions: This flag tells the recommendation algorithm to include offers where there are multiple preassigned fare products with various flexibility.

Filters

To account for different needs among clients, a set of filters can be used. Generally, the filters will do such that the offers which doesn't comform to given filters are not considered while giving recommendations. We offer filtering on the following:

  • IncludeAuthorities: Used if a client only wants recommendations for a specific operator or set of operators
  • IncludeFareClasses: Used when adding a dimension for fare classes.
  • IncludeAccommodationFacilitySet: Used when adding a dimension for facility types(sleeper, regular seat, etc)

Basic Recommendations

Remarks

  • We will only make a recommendation for a serviceJourney if we can find offers for every traveller provided.
  • serviceJourneysCoveredByOffers will indicate which serviceJourneys we managed to find recommendations for. There will be situations where we will not be able to find recommendations for every serviceJourney (for example sold out situations for one of the rides), therefore serviceJourneysCoveredByOffers is important to consider.
  • For each type of recommendation in wantedRecommendation we will return a recommendation for that type of recommendation. If we can't find any offers covering the travellers for any serviceJourney, then the offersToBuy will be empty.

Examples

InputFollowing is an example illustrating the most basic input to get recommendations. It is required to provide a recommendationCofig specifying wanted recommendations:

{
  "travelers": [
    {
      "id": "Adult-1",
      "userType": "ADULT"
    }
  ],
  "recommendationConfig": {
    "wantedRecommendations": [
      "FLEXIBLE",
      "NON_FLEXIBLE",
      "SEMI_FLEXIBLE",
      "CHEAPEST"
    ]
  }
}
Filters can also be specified in the recommendationConfig, following is an example illustrating this:

{
  "travelers": [
    {
      "id": "Adult-1",
      "userType": "ADULT"
    }
  ],
  "recommendationConfig": {
    "wantedRecommendations": [
      "FLEXIBLE",
      "NON_FLEXIBLE",
      "SEMI_FLEXIBLE",
      "CHEAPEST"
    ],
    "includeFareClasses": [
      "STANDARD_CLASS"
    ],
    "includeAuthorities": [
      "RUT",
      "OPP"
    ],
    "includeUnavailableOffers": false,
    "includeOffersWithMixedCommercialConditions": false,
    "includeMultipleUpgrades": true
  }
}
OutputFollowing is an example illustrating the output:

[
  {
    "tripPatternId": "5cf81068a8d85a00010c7a97",
    "offers": [
      {
        "id": "9c409036-6cf7-414f-919f-2648d2ff9bea"
	...
      },
      {
        "id": "9141a510-d77d-4687-854d-16233ed50de1"
	...
      },
      {
        "id": "5014cb07-1922-42c6-aa39-1a36d94c3d04"
	...
      },
      {
        "id": "b5ae3852-da64-4012-88e9-6afb7ab0bbd8"
	...
      },
      {
        "id": "b56ce489-78fe-44ae-bf98-dbc7fce0c480"
	...
      }
    ],
    "seatingCapacity": [...],
    "recommendations": [
      {
        "serviceJourneysCoveredByOffers": [
          "ENT:ServiceJourney:1-1939-1644",
          "ENT:ServiceJourney:1-5636-733"
        ],
        "typeOfRecommendation": "FLEXIBLE",
        "offersToBuy": [
          {
            "id": "9c409036-6cf7-414f-919f-2648d2ff9bea",
            "numberToBuy": 2,
            "withUpgradeProducts": ["ENT:SupplementProduct:SeatUpgrade"]
          },
          {
            "id": "9141a510-d77d-4687-854d-16233ed50de1",
            "numberToBuy": 5,
            "withUpgradeProducts": ["ENT:SupplementProduct:SeatUpgrade"]

          }
        ]
      },
      {
        "serviceJourneysCoveredByOffers": [],
        "typeOfRecommendation": "SEMI_FLEXIBLE",
        "offersToBuy": []
      },
      {
        "serviceJourneysCoveredByOffers": [
          "ENT:ServiceJourney:1-1939-1644",
          "ENT:ServiceJourney:1-5636-733"
        ],
        "typeOfRecommendation": "NON_FLEXIBLE",
        "offersToBuy": [
          {
            "id": "5014cb07-1922-42c6-aa39-1a36d94c3d04",
            "numberToBuy": 2
            "withUpgradeProducts": []
          },
          {
            "id": "b5ae3852-da64-4012-88e9-6afb7ab0bbd8",
            "numberToBuy": 2
            "withUpgradeProducts": []
          },
          {
            "id": "b56ce489-78fe-44ae-bf98-dbc7fce0c480",
            "numberToBuy": 5
            "withUpgradeProducts": []
          }
        ]
      },
      {
        "serviceJourneysCoveredByOffers": [
          "ENT:ServiceJourney:1-1939-1644",
          "ENT:ServiceJourney:1-5636-733"
        ],
        "typeOfRecommendation": "CHEAPEST",
        "offersToBuy": [
          {
            "id": "9c409036-6cf7-414f-919f-2648d2ff9bea",
            "numberToBuy": 2
            "withUpgradeProducts": []
          },
          {
            "id": "9141a510-d77d-4687-854d-16233ed50de1",
            "numberToBuy": 5
            "withUpgradeProducts": []
          }
        ]
      }
    ]
  }
]

Layered Recommendations

Layered recommendations has a more complex structure, however it is fairly simple to understand how they are built up. To get a layered recommendation, a layered recommendation configuration needs to be provided. This configuration contains which categories are wanted and which fare classes are wanted. When the recommendation service handles a layered recommendation configuration it creates a new structure of the offers, consisting of three simple steps:

  1. The offers are first grouped by which service journey combination they cover.
  2. The offers in each group are divided into the categories wanted(specified in the configuration).
  3. The offers are further divided inside each category by the fare classes specified in the configuration.

We are then left with a structure of service journey combinations, containing a set of categories, which in turn contains a set of fare classes and offers matching that category and fare class. When having this structure it is really easy to find the cheapest combination of offers for each fare class, and thus for the whole structure.

Remarks

Service Journey Organize Algorithm

This is a property in the configuration for layered recommendations. This property is available to provide the clients with different options for how the layered recommendations are generated. There are three algorithms to choose from, which greatly change how many combinations of service journeys covered are generated.

Before describing the algorithms in more detail, it is expendient to create a base example from which the algorithms can be explained.

Given a trip containing three service journeys; SJ-1, SJ-2, SJ-3, offers generates 5 offers;

  1. Offer-1: Covers SJ-1
  2. Offer-2: Covers SJ-2
  3. Offer-3: Covers SJ-3
  4. Offer-4: Covers SJ-1 + SJ-2
  5. Offer-5: Covers SJ-1 + SJ-2 + SJ-3

The three algorithms and their impact are as follows:

  1. SUBSEQUENT_COMBINATIONS - If this is selected one will create all possible variations of subsequent service journeys and create recommendations for each. Following the example above this will generate these combinations: (SJ-1), (SJ-2), (SJ-3), (SJ-1 + SJ-2), (SJ-2 + SJ-3) and (SJ-1 + SJ-2 + SJ-3)
  2. FOR_EACH_AND_GROUPED_COMBINATIONS - If this is selected each service journeys will become a combination and all of them grouped will become a combinations yielding: (SJ-1), (SJ-2), (SJ-3), (SJ-1 + SJ-2 + SJ-3)
  3. COMBINATIONS_FROM_OFFERS - If this is selected each unique service journey combination found from the generated offers are made a combination. Given the example above this would yield: (SJ-1), (SJ-2), (SJ-3), (SJ-1 + SJ-2), (SJ-1 + SJ-2 + SJ-3)
Same Ticket Change

This is a property in the configuration for layered recommendations which tells recommendations wheather recommendations should find offers where all travellers must change tickets at the same service journeys or not. Given an example where there are two travellers; one adult(A1) and one student(S1), there are two service journeys and offers has generated the following offers:

  1. Offer-1: Covers A1 for service Journey 1(SJ-1) with a price of 50NOK
  2. Offer-2: Covers A1 for service Journey 1(SJ-2) with a price of 50NOK
  3. Offer-3: Covers A1 for service Journey 1(SJ-1 + SJ-2) with a price of 70NOK
  4. Offer-4: Covers S1 for service Journey 1(SJ-1) with a price of 20NOK
  5. Offer-5: Covers S1 for service Journey 1(SJ-2) with a price of 20NOK
  6. Offer-6: Covers S1 for service Journey 1(SJ-1 + SJ-2) with a price of 90NOK

The cheapest combination of offers in this example is if the travellers bought Offer-3 + Offer-4 + Offer-5, however if same ticket change is set to true(meaning each traveller would have to change tickets at the same service journeys) the cheapest combination possible would be if the travellers bought Offer-1 + Offer-2 + Offer-4 + Offer-5.

ANY_CLASS

Some clients might also want to get a layered recommendation without regards to the fare classes, this can be achieved by providing the ANY_CLASS fare class in the layered recommendation config.

Examples

Input

{
  "typesOfRecommendation": [
    "FLEXIBLE",
    "SEMI_FLEXIBLE"
  ],
  "fareClasses": [
    "PREMIUM_CLASS",
    "STANDARD_CLASS"
  ],
  "includeMultipleUpgrades": true
}
Output

{
  "tripPattern": {...},
  "tripPatternRaw": {...},
  "offers": [
    { "id": "1", ...},
    { "id": "2", ...},
    { "id": "3", ...},
    { "id": "4", ...},
    { "id": "5", ...},
    { "id": "6", ...},
    { "id": "7", ...},
    { "id": "8", ...},
    { "id": "9", ...},
    { "id": "10", ...},
    { "id": "11", ...},
    { "id": "12", ...}],
  "recommendations": [],
  "layeredRecommendations": [
    {
      "serviceJourneysCovered": [
        "SJ-1"
      ],
      "recommendations": [
        {
          "typeOfRecommendation": "FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": [
                {
                  "id": "1",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": [
                {
                  "id": "2",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            }
          ]
        },
        {
          "typeOfRecommendation": "SEMI_FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": []
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": [
                {
                  "id": "3",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            }
          ]
        }
      ]
    },
    {
      "serviceJourneysCovered": [
        "SJ-2"
      ],
      "recommendations": [
        {
          "typeOfRecommendation": "FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": []
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": [
                {
                  "id": "4",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            }
          ]
        },
        {
          "typeOfRecommendation": "SEMI_FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": [
                {
                  "id": "5",
                  "numberToBuy": 1
                  "withUpgradeProducts": ["ENT:SupplementProduct:SeatUpgrade"]
                }
              ]
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": []
            }
          ]
        }
      ]
    },
    {
      "serviceJourneysCovered": [
        "SJ-1",
        "SJ-2"
      ],
      "recommendations": [
        {
          "typeOfRecommendation": "FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": [
                {
                  "id": "6",
                  "numberToBuy": 1
                  "withUpgradeProducts": ["ENT:SupplementProduct:SeatUpgrade"]
                }
              ]
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": [
                {
                  "id": "7",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            }
          ]
        },
        {
          "typeOfRecommendation": "SEMI_FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "offersToBuy": []
            },
            {
              "fareClass": "STANDARD_CLASS",
              "offersToBuy": [
                {
                  "id": "8",
                  "numberToBuy": 1
                  "withUpgradeProducts": []
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "seatingCapacity": [...]
}

Multidimensional Recommendations

Multidimensional recommendations has a sligthly more complex structure than the layered recommendations. In order to create multidimensional recommendations a multidimensional recommendation config is needed to be specified. This configuration contains two groups of specifications, namely dimensionspec and rulespec. The dimension spec contains information about which dimensions are wanted; which servicejourneyOrganizeAlgorithm, which types of recommendations(flexible, semi-flexible, etc), which fare classes and which accommodation facility types are wanted to be included. The rule spec contains information about which flags are set(see section above about flags). Depending on what is specified the following steps are executed to create the wanted dimensions:

  1. The offers are first grouped by which service journey combination they cover.
  2. The offers in each group are divided into the categories wanted(specified in the configuration).
  3. The offers are further divided inside each category by the fare classes, specified in the configuration.
  4. The offers are lastly divided inside each fare class by accommodation facility, specified in the configuration.

We are then left with a structure of service journey combinations, containing a set of categories, which in turn contains a set of fare classes, and then lastly the accommodation facilities which contains the matching offers for that category, fare class and accommodation facility. When having this structure it is really easy to find the cheapest combination of offers for each accommodation inside each fare class and each recommendation type.

Remarks

See remarks for layered recommendations.

ANY_FACILITY_SET

Works like the ANY_CLASS for fare classes(see section about layered recommendations), just that it is used for accommodation facilities.

Examples

Input

{
  "multidimensionalRecommendationConfig": {
    "dimensionSpec": {
      "serviceJourneyOrganizeAlgorithm": "FOR_EACH_AND_GROUPED_COMBINATIONS",
      "includeTypesOfRecommendations": ["FLEXIBLE"],
      "includeFareClasses": ["PREMIUM_CLASS"],
      "includeAccommodationFacilitySet": ["SLEEPER", "SEATING"]
    },
    "ruleSpec": {
      "sameTicketChange": false,
      "includeUnavailableOffers": false,
      "mixinOffersWithHigherFlexibility": true,
      "includeMultipleUpgrades": true
    }
  }
}
Output

{
  "tripPattern": {...},
  "tripPatternRaw": {...},
  "offers": [
    { "id": "1", ...},
    { "id": "2", ...},
    { "id": "3", ...},
    { "id": "4", ...},
    { "id": "5", ...},
    { "id": "6", ...},
    { "id": "7", ...},
    { "id": "8", ...},
    { "id": "9", ...},
    { "id": "10", ...},
    { "id": "11", ...},
    { "id": "12", ...}],
  "recommendations": [],
  "multidimensionalRecommendations": [
    {
      "serviceJourneysCovered": ["SJ-1"],
      "perTypeOfRecommendation": [
        {
          "typeOfRecommendation": "FLEXIBLE",
          "perFareClass": [
            {
              "fareClass": "PREMIUM_CLASS",
              "perFacilitySet": [
                {
                  "facilitySet": "SLEEPER",
                  "offersToBuy": [
                    {
                      "id": "714a1498-2e78-48ca-8ff4-2bba3ce51b79",
                      "numberToBuy": 1,
                      "withUpgradeProducts": ["ENT:SupplementProduct:Sleeper"]
                    }
                  ]
                },
                {
                  "facilitySet": "SEATING",
                  "offersToBuy": []
                }
              ]
            }
          ]
        }
      ]
    }
  ],
  "seatingCapacity": [...]
}

TagSpec - Deprecated (supported until November 1. 2019)

(back to Table of contents)

Introduction

The Offers-API will find every valid offer for a given trip and list of travelers, and finding a combination of offers that covers the whole trip for each traveler may be a challenging task in some cases, especially if you are searching for a specific combination like the cheapest one.

We have created a tagging mechanism to help our consumers select the optimal offers combination for any trip. The idea is to let the client specify what kind of combinations thay are interested in, and give all offers within that combination the same tag. You find the option to specify which tags you want to use in the tagSpec input parameter.

The different tagging options

We have created several different tag options so far, which can be used in combination with each other. This means that any offer can have zero, one or several tags.

  • tagCheapest: This tag will mark the combination of offers that is needed in order to achieve the lowest price for the entire group of travellers.
  • tagPrFareClass: We have a classification of fareClass which includes “Economy”, “Standard” and “Premium”, this tag will find the optimal combination (cheapest) of offers within each of these classes. sameTicketChangeForAllTravelers: This will ensure that all travellers in a party will change tickets at the same time in trips where we have several legs.

How to include offers without a tag

Most of our consumers use the tagging mechanism, and will ignore any offer without a tag. In order to reduce the amount of data we return we will by default filter out any offer that does not contain a tag. This will only apply if a client have specified at least one tag.

If you want to use the tagging mechanism without this filtering method you can override it by declaring “includeOffersWithoutTag” as true in the tagSpec specification.

The structure of an offer

(back to Table of contents)

An Offer is the offering of a product at a specific price, often related to a trip and a traveler, but not neccessarely. If a trip and a traveler is given the offer is made by finding a sales package configuration that is valid for the given trip and traveler and computing the price for that configuration. It is also possible to omit the trip and traveler and specify the parameters that should be used for the sales package configuration directly. More on sales package configurations below.

Model

The offer consists of 3 parts, the sales package configuration, the traveler validity map and the tags. The first two parts will be explained here, while the last part has its own section on this page.

Sales package configuration

Offers model

The sales package configuration is the main part of the offer. To explain what it is properly it is neccessary to explain a few things about the product model. I the model each product is split into different smaller entities. Each entity models a separate set of the products properties and rules. There are 4 main entities: sales package, fare product, validable element, fare structure element (these will hereafter often be refered to with SP, FP, VE, FSE).

Sales package: Concerned with the rules regarding distribution of the product.

Fare product: Concerned with the rules regarding different usages of the product (how its refunded, if it can be exchanged etc).

Validable element: Concerned with the rules regarding where and when the product is valid.

Fare structure element: Concerned with the pricing of the product. *

These entities are arranged into a hierarchy as shown in the figure to the right. What this means is that a VE can consist of one or more FSEs. An FP can consist of one or more VEs. And an SP can consist of one or more FPs. Common for all 4 entities is that they can be linked to parameters. The parameters are the entities that express the different rules. For example there is one parameter named UserProfile that when linked to a product expresses what types of travellers are allowed to use that product.

The parameters can be linked to a price, and by doing that they can be used to express the fare structure of a product. There will typically be a separate parameter for each differentiable step in the fare structure. For example a product a product that cost 50 kr for traveling accross 1 or 2 zones, and 70 for longer travels may be modelled with an FSE linked to 2 separate GeographicalIntervals.

Now back to the sales package configuration. A sales package configuration is a sales package where only a specific subset of the parameters are used. The product definition may contain multiple user profiles, but in a sales package configuration only the profile that is relevant for the current traveller is used. The sales package configuration follows the same structure as the model, meaning that it contains a fare product configuration for each valid product in the sales package, and so forth. Each configuration contains a reference to the entity they are based on and a ref to each of the chosen parameters if there are any. These references can be used to fetch the complete entity using the Prodducts-API if more information is needed.

*Even though each of the 4 main entities have a separate responsibility so to say, the model allows for almost all types of parameters to be linked to anyone of them (the exception is GeographicalInterval and other parameters directly related to the fare structure. Those parameters can only be linked to an FSE). This leads to the definitions of these entities to become a bit blurry sometimes.

Traveller-mapping

The travellerMapping field on the offer says which travellers are allowed to use the offer, and what user profile they received. It contains a list of TravellerValidityGroups and each group has a user profile reference and a list of traveller ids. There may be more than one TravellerValidityGroup if the offer is for a group ticket, or if the chosen user profile has a companion profile. For example for a family ticket for one child and one adult, there will be two TravellerValidityGroups in the traveller mapping. The one for adult will have a reference to the adult user profile and the ids of all the travellers that are considered adults according to that user profile.

The traveller validity group also indicate the max and min nr of travellers within that group that one offer can cover. This can be used to find the number of offers needed to cover all the travellers. If the client asks for the offers to be taged, these numbers will be used in that calculation.

Pricing

The price of the offer is the price of the sales package configuration. Each parameter and sub-configuration may contribute to the price. For example a 50% child-discount will typically be shown as a negative price contribution on the user profile for child. In the configuration-hierarchy the prices are aggregated meaning that the price of the SP-configuration will include the sum of the prices for all of its FP-configurations and the parameters linked to the SP-configuration. If a configuration has a price contribution it wil also be included in the aggregate.

Multiple legs

An offer may cover more than just one leg if the product allowes for interchanging. For some products the offers will include a configuration for the lower-level-entities (ref the figure) for each of the legs. For a trip with two legs there may for example one FSE-configuration for each leg of the trip, while still only beeing one VE-configuration. The split happens at the layer that contains the Interchanging-parameter. This can be useful if its neccessary to distribute the revenue accross the legs as it is already correcly calculated in the offer. Each configuration contains a list of service journey id to indicate which part of the total trip they are relatet to. A configuration higher up in the hierarchy will always point to the combined list of service journeys that its sub-configurations point to.

Optional products

Some sales packages may include supplement products along with the main product. The optional field on fare product configuration is used to indicate that the customer can choose whether or not they want to purchase that product when they buy the offer. The price of an optional product will not be included in the price of the sales package configuration.

Supplement products

(back to Table of contents)

A supplement product is a fare product that will provide an additional access right when used with another fare product. The additional right could be e.g. a seat reservation, an upgrade to another class, or a meal. Supplement products are by nature optional, which means that the customer can choose if they want to include them in the purchase or not. Supplement product also usually means supplement price, although they may be offered for free.

Supplement products usually have specific requirements with regards to which fare products that will qualify for offering the supplement product. As an example, a public transport operator may want to offer a supplement product for a meal on economy fares, while the standard and premium fares include the meal. Consequently, it is only relevant to present the meal supplement product for the economy fare. The meal supplement requires the consumer to be present on the train, so it is also not possible to buy the supplement product without the economy fare product that grants access to the ride. The requirements for offering a supplement product are checked by the fare calculation engine and only if all requirements are met will the supplement product be offered. The fare product and the supplement product are usually sold as a whole, meaning they are represented by two sales offer package elements in the same sales offer package. If the customer already purchased an access right (fare product) that entitles a supplement product, the pre-purchased fare product must be specified for each traveler when requesting an offer in order to have the supplement product presented as a separate offer.

The price of a supplement product

The price of an optional supplement product is not included in the price that is reported on the sales offer package. The price of the sales offer package covers all required products in the package. To find the total price for a sales offer package with a specific choice of supplement products, combine the price of each supplement product that should be included with the price of sales offer package itself.

Common supplement products and classification of supplement products

Some common supplement products are listed below:

  • Supplement products that represent seat reservations
  • Supplement products for sundry items like the right to bring extra luggage, bicycles or pets
  • Special products like reservations for large motorized wheelchairs
  • Supplement products that represent different classes of seat reservations are usually mutually exclusive when considering one ride on public transport. If a PTO wishes to support several types of reservation products that the customer may choose from (e.g. standard seat, premium seat, seat in family area) these supplement products can be typed using the TypeOfFareProduct mechanism. Supplement products with the same TypeOfFareProduct can then be grouped together when the offers are presented to the customer. The TypeOfFareProduct property is common to all FareProducts and is inherited by SupplementProduct.

Supplement products will usually have parameters describing the access right granted by the product. An example would be a supplement product granting the right to bring a bicycle, which will typically have a Luggage Allowance parameter that specifies the baggage type bicycle.

In rare cases a supplement product can be modelled as a group ticket. This would be the case if a supplement product is used to reserve a whole compartment, e.g. an optional sleeper compartment on a night train which can be used by 1-4 persons. The minimum and maximum number of persons is modelled by adding a group ticket parameter to the supplement product. Note that unlike group ticket properties on PreassignedFareProducts, SupplementProducts with group parameters do not influence the contents of the travellerMapping section or the numberToBuy in the tags section of the response. As the SupplementProduct is optional, the customer will have to make a choice on how many of the SupplementProduct that should be included in the purchase.

Capacity for supplement products

Supplement products often have limited availability, typically when there are limited number of seats/compartments or physical space for luggage onboard. Information regarding the current availability for supplement products with limited availability are included in the seatingCapacity part of the response when requesting offers. See also Seating Capacity.

The following table describes some common availability scenarios for supplement products:

Response dataInterpretation
SupplementProduct A from a SalesOfferPackage for DatedServiceJourney 123 is included in the seatingCapacity list with capacity X where X>0 for DatedServiceJourney 123SupplementProduct A is available, with X left to sell on the ride identified by DatedServiceJourney 123
SupplementProduct A from a SalesOfferPackage for DatedServiceJourney 123 is included in the seatingCapacity list with capacity =0 for DatedServiceJourney 123SupplementProduct A is sold out on the ride identified by DatedServiceJourney 123
SupplementProduct A from a SalesOfferPackage for DatedServiceJourney 123 is included in the seatingCapacity list, but capacity and/or DatedServiceJourney is not listedAn error occurred when checking availability, which means A cannot be sold temporarily. A could become available at a later time.
SupplementProduct B from a SalesOfferPackage from DatedServiceJourney 234 is not included in the seatingCapacity listProduct B has no availability limitations and is always available on the ride identified by DatedServiceJourney 234

Supplement products that apply to more than one leg

Supplement products are usually purchased separately for each ride in a trip. This allows the customer to choose different supplements for the different rides. However, in some cases a supplement product can cover two or more rides. Similar to other fare products, the serviceJourneyIds list in the fareProducts section of the response specifies which rides the supplement product apply to.

Filtering SupplementProducts

In order to reduce the number of SupplementProducts in a response, the API will by default omit SupplementProducts for luggage. To include SupplementProducts for luggage, specify the type of luggage to include in the luggageTypes section of Traveler in the request.

Selecting supplement products

Each FareProduct included in a Sales Offer Package has a unique selectableId. For optional products like SupplementProducts, use the offerId in combination with the selectableId to identify and order the SupplementPoduct in the sales and reservation APIs.

Note: If a trip consisting of several rides do not offer the same type of luggage for all rides (either as a parameter on the fare product describing included access rights or as a separate supplement), the customer may not be able to buy any combination of products that provides them the necessary access rights to bring the luggage on the entire journey. The clients of the offer API should pay attention to such scenarios and try to inform and guide the customer to make the best possible choice in such situations.

Seating capacity

(back to Table of contents)

Seating capacity shows the number of available seats for fare products that require seat reservation for a given departure. However, it is worth noting that seating capacity is indicative and does not contain a complete list of capacity. It is only a snapshot of capacities required for the offers retrieved. A product is present in the seating capacity response if the product configuration has specified that it has compulsory reservation or if there are quotas specifying that a product needs to be reserved. Clients can assume that every item in the list of seating capacity needs to be reserved. An element in seating capacity has the following fields:

  • fareProductId: the id of the fare product
  • datedServiceJourneyId: a unique id for the departure
  • capacity: the sum of available seats for the fare product for this departure
  • status: OPEN/CLOSED/UNKNOWN

Seating capacity can be fetched from three endpoints:

  • POST /search/trip
  • GET /search/trip/trip-pattern/<tripId>
  • PATCH /search/trip/trip-pattern/<tripId>

Examples

TypicalIn this example, the products with compulsory reservation are Ticket, SeatUpgrade and OtherTicket. The seating capacity is for one two legs:

"seatingCapacity": [
   {
      "fareProductId": "ENT:PreassignedFareProduct:Ticket",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 25,
      "status": "OPEN"
   },
   {
      "fareProductId": "ENT:SupplementProduct:SeatUpgrade",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0,
      "status": "OPEN"
   },
   {
      "fareProductId": "ENT:PreassignedFareProduct:OtherTicket",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0
      "status": "UNKNOWN"
   },
   {
      "fareProductId": "ENT:PreassignedFareProduct:Ticket",
      "capacity": 0,
      "status": "UNKNOWN"
   }
]
The first three elements are for the same leg, and capacity was found for Ticket and SeatUpgrade(sold out), however no information was found about OtherTicket, which is why the status is UNKNOWN. The fourth element is for the second leg, but no dated service journey id was found. Offers isn’t able to find capacity without a dated service journey id.
Closed departuresAnother case which can occur is closed departures, this means that the operator have closed the departure and nothing should be sold on that departure. The capacity would look something like this:

"seatingCapacity": [
   {
      "fareProductId": "ENT:PreassignedFareProduct:Ticket",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0,
      "status": "CLOSED"
   },
   {
      "fareProductId": "ENT:SupplementProduct:SeatUpgrade",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0,
      "status": "CLOSED"
   },
   {
      "fareProductId": "ENT:SupplementProduct:OtherTicket",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0,
      "status": "CLOSED"
   },
   {
      "fareProductId": "",
      "datedServiceJourneyId": "ENT:DatedServiceJourney:5650328",
      "capacity": 0,
      "status": "CLOSED"
   },
]

Quota

(back to Table of contents)

Quota is the remaining availability for a given serviceJourney, it is generated based on the Stock object, see here, which is collected from the Inventory module. It is comprimised of two fields:

  • serviceJourneyId: which is the id of the relevant service journey for which the quota is appliccable.
  • stock: is the remaining availability of the element, or rather the product for which it is located under. It is generated by the aggregatedAvailability property of the Stock object, and thus shows the total availability including e.g. overflow.
An example for how the quota object could be represented in the offer response

"fareStructureElements": [
    {
        "id": "ENT:FareStructureElement:KMInterval",
        "version": "ENT:Version:1",
        .
        .
        .
        "serviceJourneyIds": [
            "ENT:ServiceJourney:123"
        ],
        "datedServiceJourneys": [],
        "interval": {
            "id": "ENT:GeographicalInterval:100KM",
            "version": "ENT:Version:1",
            "name": [],
            "description": [],
            "distance": 100,
            "zones": []
        },
        "quotas": [
            {
                "serviceJourneyId": "ENT:ServiceJourney:123",
                "stock": 10
            }
        ]
    }
]

Entitlement products

(back to Table of contents)

Entitlement products are products that can give travellers certain benefits when buying or consuming other products. Users of the Offers-API can specify that a traveller owns an entitlement product by providing the id of the entitlement product in the PreownedProductSpec-attribute on Traveler.

Example

"travelers": [
    {
        "id": "traveler1",
        "userType": "ADULT"
        "products": [
            {
                "id": "ENT:EntitlementProduct:CustomerCard"
            }
        ]
    }
]

In order for an entitlement product to give a discount to another product there must exist a sale discount right that is linked to the entitlement product and to the main product. Sale discount rights are separate products that must be valid for the specified trip and the traveller holding the entitlement product for it to be applied to the main products. If a sale discount right has been applied to a product, then the fare product configuration will contain information about the the sale discount right under the field FareProductConfiguration.discountRight. The price on the sale discount right configuration is is the total discount given. This amount will be included the price of the main product.

Information on how to find entitlement products for a traveler can be found in the documentation for the Benefits-API: https://developer.entur.org/pages-benefits-docs-benefits-api

Example of an Offer with an applied sale discount right

{
  "id": "aabe8409-b439-47a4-8a8d-db2b92ee1023",
  "salesPackageConfig": {
    "id": "ENT:SalesPackage:Flex",
    "version": "ENT:Version:SP-Flex-1",
    "prices": [
      {
        "amount": "0", // Total price for the offer
        "currency": "NOK"
      }
    ],
    "priceContributions": [
      {
        "amount": "0.00",
        "currency": "NOK"
      }
    ],
    ...,
    "fareProducts": [
      {
        "id": "ENT:PreassignedFareProduct:Flex",
        "version": "ENT:Version:PFP-Flex-1",
        "prices": [
          {
            "amount": "0.00", // Total price for the main product, including the discount from the sale discount right
            "currency": "NOK"
          }
        ],
        "priceContributions": [
          {
            "amount": "0.00",
            "vatGroup": "TRANSPORT_AND_TICKETS_VAT",
            "currency": "NOK"
          }
        ],
        "validableElements": [
          {
            "id": "ENT:ValidableElement:Flex",
            ...,
            "prices": [
              {
                "amount": "500.00", // The base price of the product.
                "currency": "NOK"
              }
            ],
          }
        ],
        ...,
        "discountRight": {
          "id": "ENT:SaleDiscountRight:FlexDiscount",
          "version": "V1",
          "prices": [
            {
              "amount": "-500", // Total discount
              "currency": "NOK"
            }
          ],
          "priceContributions": [
            {
              "amount": "0.00",
              "currency": "NOK"
            }
          ],
          "parameters": {
            "entitlementGiven": [
              {
                "id": "ENT:EntitlementGiven:Flex",
                "version": "1",
                "prices": [
                  {
                    "amount": "-531",
                    "currency": "NOK"
                  }
                ],
                "priceContributions": [
                  {
                    "amount": "-531",
                    "currency": "NOK"
                  }
                ],
                "optional": false
              }
            ]
          },
          ...,
        }
      }
    ]
  },
  "travelerMapping": [
    {
      "travelerIds": [
        "a8333352-dec7-4514-a982-91a0660888d7"
      ],
      "maxNumberOfTravelers": 1,
      "minNumberOfTravelers": 1,
      "userProfileId": "ENT:UserProfile:Adult",
      "userProfileIds": []
    }
  ],
  "tags": []
}

Tickets for extended journeys

(back to Table of contents)

If a traveller has a valid ticket but wishes to travel further than the original ticket is valid, some operators offer a ticket for the additional journey at a reduced rate. A specialized endpoint is available that will calculate the necessary ticket extension, considering the total journey and the ticket that the traveller already has.

The most common case is where the active ticket is a period ticket or travel pass, and the extension is a one-time journey. The extension endpoint will calculate a new offer for an extension even if the active ticket is a single use ticket. Note that if the active ticket is an exchangeable single use ticket, it is probably more appropriate to offer the customer guidance on the exchange process rather than offering an extension ticket.

Not every operator offers the possibility to buy a reduced rate ticket for extending a journey but rather requires the customer to buy a normal single ticket for the extension part of the journey. The extension endpoint will produce an offer for a normal single use ticket as well as a reduced price offer (if any such product exists). In any case the offers given will provide the best match for the part of the journey for which the customer needs to buy a new ticket. If there is no product that can cover the total of the requested journey in combination with the active ticket, the resulting response from the extension endpoint will be empty. To summarize, the extension ticket endpoint will in most cases deliver 1 or 2 offers, of which the customer may choose the offer with the lowest rate.

The extension endpoint is designed to handle only one traveller at the time, but can facilitate offers for several trip patterns if there are several options for traveling that are of interest to the traveller.

The extension endpoint targets travel within the network of a single operator. It is out of scope for the extension endpoint to provide offers for a trip that involves journeys with multiple transport operators.

Please note that an extension ticket do not replace the original ticket. Both tickets are required to document the right to travel on the entire journey.

Extension tickets can be calculated by the endpoint

  • POST /search/extensions/trip

The product id and the validity of the existing ticket is described on the traveler.

NOTE: In order to compute the correct number of zones for the trip, the tripPattern must include information about the route traveled represented by points on link information on each leg. See the Journey Planner documentation for more information on points on link and tripPatterns. Although this is only required for some zone based products we recommend always including pointsOnLink in the tripPatterns used to request offers.

Example

"travelers": [
    {
        "id": "traveler1",
        "userType": "ADULT"
        "products": [
            {
                "id": "RUT:PreassignedFareProduct:Ruter30Days",
                "zones": [
                "RUT:TariffZone:2V"
                ]
            }
        ]
    }
]