This is the E2E documentation for the SBB Swiss Mobility API (B2P API) which describes the concepts, services, booking flow, access, authorization and error handling.

The services are described briefly from the business perspective. The technical details of the services (methods, request, responses, mandatory parameters etc) and a service mock are available on the Swagger UI page:

20 Swagger UI Integration (Preview of upcoming features)

1. Registration

The B2P web service is secured by an API Management Platform. To get access to the services you need to register as a developer on the SBB developer portal. There is also an integration developer portal that may be used for testing purposes. Please first register for the integration environment.

Before you will get access to the production environment you need to successfully integrate the services on the integration environment to show compliance with the rules.

For logging into the developer portal you need to have an account at one of the following provides:

  • Azure (only for SBB internal use)

  • Swisspass

When logging in with one of these identity provides you will be asked to share a minimum of information like your e-mail address. Once registered you will find the B2P API as following:

3scale b2p

As a first step you need to choose a plan. The plan defines usage limits and which services you are allowed to call. All plans need SBB approval. The plans can be changed later (needing additional approval).

3scale b2p plans

When registering your Client, you will be asked for a name and description. Those values can be chosen freely, but we recommend you to use something descriptive. Choose authentication provider "Azure AD". Once you finished the registration process your client should be in a Pending state until it gets activated by the SBB. Even though it is not activated yet you can already see your login credentials. Since the B2P web service uses the OAuth 2 client credentials flow you will get a Client ID, a Client Secret and a scope. There is no need to provide a Redirect URL.

b2p azure client

Before you can start using the web services you need to contact SBB to get your personal contract ID that has to be send with every request.

Please note that the Swagger UI within the developer portals are for documentation purposes only. When expecting mocked responses one should use the 20 sandbox environment.

2. Authorization

The B2P web service is secured using OAuth 2. Therefore, you need to obtain a token before sending requests to one of the services. All auth tokens are valid for 60 minutes and should be reused for subsequent requests. We recommend you to use existing libraries for your implementation, but to validate your credentials and to do some initial tests you can use a tool like Postman or Curl. The first step is to obtain a token (JWT) and to set it as a header in your actual request. For obtaining a token you need your Client ID and Client Secret (see Registration). You can obtain the token from:

Refresh tokens are not supported.
production and integration environment
https://login.microsoftonline.com/2cda5d11-f0ac-46b3-967d-af1b2e1bd01a/oauth2/v2.0/token
Example: Obtain Token
curl -X POST \
 'https://login.microsoftonline.com/2cda5d11-f0ac-46b3-967d-af1b2e1bd01a/oauth2/v2.0/token' \
 -d 'grant_type=client_credentials&client_id=$clientId&client_secret=$clientSecret&scope=$scope'

The token is encapsulated within the result as an access_token and needs to be set as a Bearer token in the Authorization-HTTP-Header as following:

Example: Location Request with token
 curl -X GET \
  'https://b2p.api.sbb.ch/api/locations?name=Bern' \
  -H 'authorization: Bearer $accessToken' \
  -H 'cache-control: no-cache' \
  -H 'accept: application/json' \
  -H 'x-contract-id: ABC1234' \
  -H 'x-conversation-id: e5eeb775-1e0e-4f89-923d-afa780ef844b'
The URL for the B2P production environment is https://b2p.api.sbb.ch whereas the integration environment is available under https://b2p-int.api.sbb.ch.

2.1. Refresh Token

Refresh tokens are not supported. Instead, a new token should be issued before the old one expires.

3. General Concepts

3.1. Web Service

The B2P web service is a RESTful web service that uses HTTP methods for accessing resources.

The following parameters must be set in the HTTP Header with every request:

  • The Conversation-ID (x-conversation-id) to identify the current sales process

  • The partner’s contract ID (x-contract-id) to identify the partner, permissions and preferences

Example: Mandatory x-contract-id and x-conversation-id parameters set in HTTP Header
curl -X GET \
 'https://b2p.api.sbb.ch/api/locations?name=Bern'
 -H 'accept: application/json' \
 -H 'accept-language: en' \
 -H 'x-conversation-id: cafebabe-0815-4711-1234-ffffdeadbeef' \
 -H 'x-contract-id: ABC1234'

3.2. Conversation-ID

The Conversation-ID is an unique identifier of a sales process. Every step (get offers, booking, get tickets, etc.) within one sales process must share the same Conversation-ID.

The Conversation-ID is a mandatory parameter and must be send in the HTTP header as x-conversation-id with every request.

The Conversation-ID must match the UUID format.
For example: cafebabe-0815-4711-1234-ffffdeadbeef.

It is crucial that a new Conversation-ID is used when a new sales process begins. Some steps will fail if the Conversation-ID is reused (e.g. payment).

If however the user requests multiple offers and multiple prebookings the Conversation-ID should be reused, as he is still in the same sales process.

3.3. Versioning

The services are versioned and the service version can bee seen in the URL. No visible version means it’s V1. Example:

The services are versioned independently of each other. It’s unproblematic for example to use Locations V1 together with Prebooking V2.

Service versions that aren’t published on the production environment but only on the integration environment are subject to change. As soon as a version is available on the production environment there won’t be any breaking changes. However, it’s possible that optional request or response parameters will be added.

The general rule is that there may be a new version every 3 weeks and old version may be removed from the API after 9 weeks.

Most services return links to guide you through the booking flow and tell you which services can be called next. Here is an extract from a trip-offers response.

Link section of the trip-offers response
"links":
[
    {
        "rel": "prebook", (1)
        "version": 2, (2)
        "href": "https://b2p.api.sbb.ch/api/v2/prebookings", (3)
        "method": "POST", (4)
        "contentTypes": [
            "application/json" (5)
        ]
    },
    {
        "rel": "online-offers",
        "version": null,
        "href": "https://www.sbb.ch/de/kaufen/pages/fahrplan/fahrplan.xhtml?[...]", (6)
        "method": "GET",
        "contentTypes": [
            "text/html"
        ]
    }
]
1 Name of the service.
2 Version - if the service is versioned.
3 Service URL.
4 HTTP Method.
5 Returned media type.
6 This link redirects the user to the SBB webshop to perform the next steps.

Placeholders

Links may contain placeholders that must be filled in. Below is an example of a trip response.

Link section of trip response
{
    "rel": "trip-offers",
    "version": null,
    "href": "https://b2p.api.sbb.ch/api/trip-offers?tripId=qgTwSQoCVjESxgLCtkhLScK2VCRBPTFATz1aw7xyaWNoIEhCQFg9ODU0MDIxMUBZPTQ3Mzc4MTc3QHU9MEBMPTg1MDMwMDBAYT0xMjhADTswQmVybkBYPTc0MzkxMwU1GDY5NDg4MzQZNQQ3MBk1LDIwMjMwMjExMTAzMhkNPDEyOCRJQyAxIDcxNCQkMSQFAeDCtktDwrYjVkUjMiNDRiMxMDAjQ0EjMCNDTSMwI1NJQ1QjMSNBTSMxNjQ2NSNBTTIjMCNSVCMxNSMBOQ06KDAjRVJHIzEjSElOAQwgQ0sjOTI3OTJ8EQYQODQ4fDkFBhQwfDB8ODUFGSQwfDF8MHw0MTE0BRY8LTIxNDc0ODM2NDgjwrZLUhFcLDEjGtoBCjoKEgoHOCkauBIHU1RBVElPThIZMjAyMy0wMi0xMVQxMDozMjowMCswMTowMCIHUExBTk5FRCgIFTwhIRU8ABoyPAAMMToyOEo8APBAChIYCAgQChoGMDAwMDExIgJJQyoBMTIDNzE0GjYKAlIgEhpQbGFjZSByZXNlcnZhdGlvbiBwb3NzaWJsZRj___8JAgQBIAkITP___wEqDlBVQkxJQ19KT1VSTkVZ&passengers=${passengerInfos}",(1)
    "method": "GET",
    "contentTypes": [
        "application/json"
    ]
},
{
    "rel": "online-offers",
    "version": null,
    "href": "https://www.sbb.ch/de/kaufen/pages/fahrplan/fahrplan.xhtml?recon=VCUyNEElM0QxJTQwTyUzREJlcm4lNDBYJTNENzQzOTEyMiU0MFklM0Q0Njk0ODgyNSU0MEwlM0Q4NTA3MDAwJTQwYSUzRDEyOCU0MCUyNEElM0QxJTQwTyUzRFolQzMlQkNyaWNoK0hCJTQwWCUzRDg1NDAxOTMlNDBZJTNENDczNzgxNzclNDBMJTNEODUwMzAwMCU0MGElM0QxMjglNDAlMjQyMDE4MDgyMzEwMzIlMjQyMDE4MDgyMzExMjglMjRJQysxKysrKyUyNCUyNDElMjQlQzIlQjZLQyVDMiVCNiUyNTIzVkUlMjUyMzAlMjUyM0NGJTI1MjMxMDAlMjUyM0NBJTI1MjMwJTI1MjNDTSUyNTIzMCUyNTIzU0lDVCUyNTIzMSUyNTIzJUMyJUI2S0NDJUMyJUI2JTI1MjNWRSUyNTIzMCUyNTIzRVJHJTI1MjMxJTI1MjNISU4lMjUyMzAlMjUyM0VDSyUyNTIzMzcyMTUyJTI1N0MzNzIxNTIlMjU3QzM3MjIwOCUyNTdDMzcyMjA4JTI1N0MwJTI1N0MwJTI1N0M4NSUyNTdDMzcyMTQyJTI1N0MxJTI1N0MtMjE0NzQ3OTUzNCUyNTdDMCUyNTIz&datum=2018-08-23&zeit=1032",(2)
    "method": "GET",
  "contentTypes": [
    "text/html"
  ]
}
1 Link with the ${passengerInfos} placeholder that must be filled in with passenger information.
2 URL without placeholders. The link points to the www.sbb.ch website, where the passenger information will be entered by the user.

Message Body and optional parameters

Neither the body of a POST request nor the optional parameters are shown in the links. The body structure and the optional parameters can be seen in the Swagger UI.

Example: Trip-offers request with filled in placeholders
 curl -X GET \
  'https://b2p.api.sbb.ch/api/trip-offers?tripId=zwVECgJWMRLSAsKswrZCQUlNwrZ0AQvwQEhLScK2VCRBPTFATz1CYXNlbCBTQkJAWD03NTg5NTY2QFk9NDc1NDc0MDhAdT0wQEw9ODUwMDAxMEBhPTEyOEAkDToIZXJuATUUNDM5MTMxATUYNjk0ODgzNBk1DDcwMDARNSwyMDIzMDIyNDE3NTYZDTg4NTYkSUNFIDM3MSQkMSQFAeDCtktDwrYjVkUjMiNDRiMxMDAjQ0EjMCNDTSMwI1NJQ1QjMSNBTSMxNjQ2NSNBTTIjMCNSVCMxNSMBOQ06KDAjRVJHIzEjSElOAQwgQ0sjOTE3OTZ8EQYEODUBDAEGGDB8MHwxNjUFGiQ0fDF8MHw0MTE0BRc8LTIxNDc0ODM2NDgjwrZLUhFdLDEjGvMCCjoKEgoHOCkawBIHU1RBVElPThIZMjAyMy0wMi0yNFQxNzo1NjowMCswMTowMCIHUExBTk5FRCgQCh8VPAQyMxU8ACIVIQARGSEIMjE4SiEAJBIKGwoOCgMxMzJKHQAAExWbIYAVIQAaMpsAADhWmwDwQxQSFggQEBQaBjAwMDAxMSIDSUNFMgMzNzEaOgoCVlISHkJJQ1lDTEVTOiBSZXNlcnZhdGlvbiByZXF1aXJlZBj_____BQMEASAFBwEBQAEaNgoCUiASGlBsYWNlIHJlGTgccG9zc2libGVWOAA8Kg5QVUJMSUNfSk9VUk5FWQ%3D%3D&passengers=paxa%3B42%3Bhalf-fare' \
  -H 'accept: application/json' \
  -H 'accept-language: en' \
  -H 'x-conversation-id: cafebabe-0815-4711-1234-ffffdeadbeef' \
  -H 'x-contract-id: ABC1234'

3.5. Swagger documentation & Service Mock

The services are described in detail using Swagger. The Swagger UIs are available here:

20 Swagger UI Integration (Preview of upcoming features)

Swagger describes every service, its request and response parameters and possible errors.

In addition, we have implemented a service mock. It does not call the real backend but returns a predefined response. You can click Try it out! in the Swagger UI to play around with the services and get an idea how they work.

4. Sales process

The sales process is a sequence of steps in the booking flow as shown in the figure below.

Sales Process Diagram
Figure 1. Sales Process

The sales process typically begins with the offer requests, however there may be some previous steps needed to perform the offer request depending on the offer request type.

There are three offer request types that initiate a sales process:

Route based

Offers for a specific origin and destination will be returned. See Route Offers for details.

Trip based

Offer for a specific trip (i.e. train connection) will be returned. See also Trip Offers and Group Trip Offers for details.

Product based

Offers for a specific product will be returned. See Product Offers and Group Product Offers for details.

Payment step can be omitted by all non retailers. See Payment for details.

4.1. Offers

Common Concepts
Offer Container

Offers are linked to offer containers. These containers indicate which offers have to be prebooked in one transaction in order to fulfil the client’s request. The following principles are respected when building offer containers:

  • All offers have the same Class of Service

  • All passengers are eligible to travel on the same route

  • Total price corresponds to the total price of all contained offers

Validity

B2P Offers are valid for prebooking for 15 minutes only. However, it cannot be guaranteed and may be shorter in times in case of a heavy system load (Eviction policy).

Please be aware that the price is not guaranteed within this period. This guarantee is only subject to prebooking.
Please respect this limitation in the offer process.
IP Extension tickets

If you are requesting an offer for a trip which is part of an international journey, ipExtensionTicket must be set to true. We offer two specific products for connection tickets. The fare and behavior is the same as regular tickets (125) and saver tickets (4004). By setting true only those two products are returned.

Passenger

The passenger id cannot be longer than 50 characters.

Trip Offers

The B2P Service operation to retrieve offers based on a given tripId for individual travelers.

Please use the B2P trips service operation to get the required tripId.

The system calculates and returns only the best offers (in terms of service and price) that match the traveler wish.

The returned offer may be different for each traveler depending on its age or reduction card. For example, a request with two travelers, one adult and one child, may result in an offer container with two offers; city ticket for the adult and a day pass for the child.

With the addition of the travelers dogs and bikes, passengers is not a required field for trip offers anymore. However either one of the dogs, bikes or passenger must be set. Otherwise a request-parameter-invalid problem will be returned. In contrast to the passengers, a dog or a bike is only requested with an id.

See 20 trip-offers-controller for details.

Affiliate

As an affiliate partner you can use the B2P web service trip offers responses to publish deep links into the SBB Webshop to potential customers.

Example for webshop affiliate deep link:
"links": [
      {
        "rel": "online-offers",
        "version": null,
        "href": "https://www.sbb.ch/de/kaufen/pages/fahrplan/fahrplan.xhtml?recon=VCUyNEElM0QxJTQwTyUzREJlcm4lNDBYJTNENzQzOTEyMiU0MFklM0Q0Njk0ODgyNSU0MEwlM0Q4NTA3MDAwJTQwYSUzRDEyOCU0MCUyNEElM0QxJTQwTyUzREx1emVybiU0MFglM0Q4MzEwMTY4JTQwWSUzRDQ3MDUwMTcwJTQwTCUzRDg1MDUwMDAlNDBhJTNEMTI4JTQwJTI0MjAxODA4MjgxMDM2JTI0MjAxODA4MjgxMjAzJTI0UkUrNDM2NyslMjQlMjQxJTI0JUMyJUE3VCUyNEElM0QxJTQwTyUzREx1emVybiU0MFglM0Q4MzEwMTY4JTQwWSUzRDQ3MDUwMTcwJTQwTCUzRDg1MDUwMDAlNDBhJTNEMTI4JTQwJTI0QSUzRDElNDBPJTNEWiVDMyVCQ3JpY2grSEIlNDBYJTNEODU0MDE5MyU0MFklM0Q0NzM3ODE3NyU0MEwlM0Q4NTAzMDAwJTQwYSUzRDEyOCU0MCUyNDIwMTgwODI4MTIxMCUyNDIwMTgwODI4MTI1NiUyNElSKzcwKysrJTI0JTI0MSUyNCVDMiVCNktDJUMyJUI2JTI1MjNWRSUyNTIzMCUyNTIzQ0YlMjUyMzEwMCUyNTIzQ0ElMjUyMzAlMjUyM0NNJTI1MjMwJTI1MjNTSUNUJTI1MjMxJTI1MjMlQzIlQjZLQ0MlQzIlQjYlMjUyM1ZFJTI1MjMwJTI1MjNFUkclMjUyMzQ1MDU4JTI1MjNISU4lMjUyMzAlMjUyM0VDSyUyNTIzMzc5MzgwJTI1N0MzNzkzNTYlMjU3QzM3OTQ5NiUyNTdDMzc5NDk2JTI1N0MwJTI1N0MwJTI1N0MxNjUlMjU3QzM3OTM0MiUyNTdDMSUyNTdDLTIxNDc0ODM2MzIlMjU3QzAlMjUyMw==&datum=2018-08-28&zeit=1036",
        "method": "GET",
        "contentTypes": [
          "text/html"
        ]

If you are subscribed to the B2P affiliation program you have to include your affiliate ID as an URL parameter (see swagger documentation for details).

Special Cases:

You will be able to sell 1 day travelpasses. These passes can be returned as an offer at trip-offers in the case a 1 day travelpass is cheaper than a regular ticket. In case you offer your client to search for return trips be aware of this:

Example: March 15 Zurich – Geneva, regular fare CHF 100.00 March 15 Geneva – Zurich, regular fare CHF 100.00

1 day travelpass CHF 80.00 You have to ensure that you don’t sell a daypass per way!

Not for every journey it is possible to sell a regular ticket (productID 125). For journey within a network (association) you have to sell a “network fare”. Nor regular tickets will be offered via our API. There exists separate products per network (for ex. productID 1560 for unireso network). The logic of the sales process is exactly the same as for the regular ticket but the validity of these tickets are different.

In some cases there will be information on trip conditions. For example in case of construction work.

Group Trip Offers

The B2P Service operation to retrieve offers based on a given tripId for traveler groups.

Group trip offers work in the same way as the regular trip offers, except that you can request offers for groups of 10 or more travelers. The offered products will be group tickets.

Affiliate links are not supported for group trips.

See 20 trip-offers-controller for details.

Product Offers

The B2P Service operation to retrieve offers for a given productId for individual travelers.

Some offers do not require a trip. These can be sold using the product-offers service operation. The product-offer request requires a productId and returns only offers with the requested product. The productId for a specific product can be obtained via the B2P products service.

Some Products do not need a passenger to be provided at the offer step (e.g. dog day-pass, bicycle day-pass). Those products can be identified easily, as there is no customer segment with the travellerType person defined. However, at the prebooking step the passenger has to be provided.

With the addition of the travelers dogs and bikes, passengers is not a required field for product offers anymore. However either one of the dogs, bikes or passenger must be set. Otherwise a request-parameter-invalid problem will be returned. In contrast to the passengers, a dog or a bike is only requested with an id.

See 20 product-offers-controller for details.

Group Product Offers

The B2P Service operation to retrieve offers for a given productId for traveler groups.

Group product offers work in the same way as the regular product offers, except that you can request offers for groups of 10 or more travelers. The requested product must be a group ticket. You can check which customer segments can be selected for a product using the products service.

See 20 product-offers-controller for details.

Route Offers

The route offer request is similar to the trip offer but does not require a tripId. Instead the UIC code of the origin, destination (and via) must be provided. The UIC codes can be obtained from the location service.

Note that supersaver tickets are never returned for the route offer request, because supersaver tickets are bound to a specific train (and so to a tripId).

With the addition of the travelers dogs and bikes, passengers is not a required field for route offers anymore. However either one of the dogs, bikes or passenger must be set. Otherwise a request-parameter-invalid problem will be returned. In contrast to the passengers, a dog or a bike is only requested with an id.

See 20 route-offers-controller for details.

Offer Options

There are several flags for controlling the way how offers are bundled and filtered in the backend. Those flags do not appear on the API documentation, as they are not part of the business requirements but only exposed for special purposes. At the moment the following options can be set as HTTP headers when requesting offers (if not set the default behaviour is used):

  • X-Offer-Options-Disable-Business-Filters [TRUE/FALSE]: This options force disables the offer filter logic of the backend. By default unuseful offers are dropped and not send in the B2P response e.g. as they may be too pricy for the requested journey. By setting this option to TRUE no offers will be dropped by the backend.

  • X-Offer-Options-IP-Extension-Ticket [TRUE/FALSE]: This option can be set when requesting specifically for international connection tickets. Please note that this behaviour has to be enabled per contract.

4.2. Prebookings

Offer prebookings

General

The offers-prebooking service makes an 'exclusive reservation' of an offer for the user until he finishes the booking process. It is especially important for limited offers such as supersaver tickets and seat reservations.

Common concepts
Offer container

All offerIdentifier’s from one offer-container must be prebooked together in one transaction.

Validity

The prebooked offer is valid for 30 minutes. After that it will be automatically discarded.

Integrity

A preBookingId will be returned for each prebooked offer.In case of an error the whole request will be invalidated. In other words, if the offer A and the offer B is prebooked in one request it is guaranteed that either two preBookingIds will be returned or an error.

The preBookingId is later used in the sales and booking processes.

The message body structure for the prebooking can be seen in the 20 prebookings-controller.

Passenger

Note that the id of the passenger in prebooking request, must be identical to the passenger id in the offer request:

Sample offer request message payload
{
  "passengers": [
    "PaxId1;33;half-fare" (1)
  ],
  "validFromDate": [
    "2021-05-21"
  ],
  "validFromTime": [
    "14:52"
  ]
}
Sample PreBooking message payload
[{
  "offerPrebookings": [{
    "offerIdentifier": "6575552"
  }],
  "passenger": {
    "id": "PaxId1", (1)
    "firstname": "John", (2)
    "lastname": "Doe", (2)
    "dateOfBirth": "1988-05-18" (3)
  }
}]
1 The passenger id cannot change between offer request and prebooking request and is limited to 50 characters.
2 The firstname and lastname are limited to 30 characters.
3 The dateOfBirth must reflect the age of the passenger from the offer request.
Dog and Bike

When requesting offer prebookings for dogs and bikes, the id of the dog/bike object must match the id of the dog/bike from the offer (1). Additionally there has to be set an owner for dog/bike in the prebooking offers request (2).

Sample offer request message payload for bike
{
  "bikes": [
    "BikeA" (1)
  ],
  "validFromDate": [
    "2022-08-21"
  ],
  "validFromTime": [
    "14:52"
  ]
}
Sample PreBooking message payload for bike
[{
  "offerPrebookings": [{
    "offerIdentifier": "6575552"
  }],
  "bike": {
      "id": "BikeA", (1)
      "owner": { (2)
        "firstname": "John",  (3)
        "lastname": "Doe", (3)
        "dateOfBirth": "1980-05-20" (4)
      }
    }
}]
1 The dog/bike id is limited to 50 characters.
2 The firstname and lastname are limited to 30 characters.
3 The dateOfBirth must reflect the age of the owner.
Group Offers

Although group trip/product offers are requested for passenger groups instead of individual passengers, a passenger must still be given when prebooking the group offer. This passenger will be the contact customer for the journey, their name may be shown on the ticket. The passenger ID may be chosen freely.

Group offers typically require a GRUPPENNAME (group name) sales parameter. The group name may be chosen freely by the customer. It is shown on the ticket. The group offer response will contain the GRUPPENNAME sales parameter if it is required. In this case, the value must be given as part of the prebooking request.

Sample Group Offer PreBooking message payload
[{
  "offerPrebookings": [{
    "offerIdentifier": "6575552",
    "salesParameter": {
        "GRUPPENNAME": "Example Group Name" (1)
    }
  }],
  "passenger": {
    "id": "PaxId1", (2)
    "firstname": "John",(3)
    "lastname": "Doe",(3)
    "dateOfBirth": "1988-05-18"
  }
}]
1 The group name is limited to 30 characters.
2 The passenger id is limited to 50 characters.
3 The firstname and lastname are limited to 30 characters.

4.3. Payment

Prior to booking, the payment step prepares the payment of one or more prebooking ids. This step is mandatory for all retailers.

The B2P web service currently supports two kinds of payments:

  • Invoice payment (is the payment method for all retailers)

  • Partner uses their own payment process (non retailers)

Overview

Payment Process Diagram

Invoice payment

The invoice payment will trigger an invoice that will be sent to the partner / retailer. Starting from version 2 of the payment service there are two optional reference fields available that may be set to internal references.

Only Swiss Francs (CHF) are accepted in payment.

See 20 payments-controller for details.

4.4. Booking

After an offer (or offers) was prebooked (and the payment was successful) it can be booked.

The api requires a list of prebookingIds to be sent as an array in the body of the POST request and returns one bookingId plus a section for each prebookingId the corresponding ticket as the ticketId.

See the 20 bookings-controller for details.

Clients should retry the booking if the request fails due to a client-side or gateway timeout. This is important because even if the call times out, the backend might still complete the booking successfully and record the sale. See Timeouts and Retry for details.
A booking request is limited to 80 prebookingIds. Booking requests with more than 80 prebookingIds will be rejected by the B2P API and a too-many-prebooking-ids problem will be returned.

A booking status can be sent when booking a prebooking. This can be either "PENDING" or "COMMITTED".

Example:

{
    "prebookingIds": [1234],
    "status": "PENDING"
}

Status COMMITTED

If no status or the status "COMMITTED" is sent, the booking is set to the status COMMITTED immediately and cannot be deleted afterwards.

Status PENDING

If the status is PENDING, a booking can be deleted up to 20 minutes after it has been booked. This can be done over the 20 delete-booking call.

A booking can be committed earlier instead of waiting for the 20 minutes to pass until the booking is automatically moved to the status COMMITTED. Use the 20 patch-booking call to commit a booking manually.

This committing is entirely optional, as the system will automatically commit the booking if neither a deletion nor a commit is sent. Do note the limitation below though.
A booking in a PENDING state cannot be refunded through the aftersales refund. Only bookings in a COMMITTED state can be successfully refunded.
Please be aware of the impact to flex products: The activation of the single travel days can not be done while the booking is PENDING state.

4.5. Fulfillment

After the successful booking, the tickets can be downloaded.

All tickets are available for download until one year after travel or latest validity date.

Tickets are available in several formats. Just define the accepted format as HTTP header information, i.e. "Accept": "application/pdf" to get a PDF. We recommend to download all available formats and cache them on your side.

Content type Ticket type

application/pdf

PDF (client needs to print the document)

text/html

Screen Ticket (client can show the ticket on his device or print it)

application/vnd.apple.pkpass

Wallet (to store in a mobile wallet vault)

The bookingId and the ticketId must be provided in the url path to download the ticket. Additionally, the legId is required in case of reservation products. See the 20 bookings-controller/getTicketUsingGET for details.

Tip: Use the fulfil link provided in the POST /bookings response which points to the ticket download endpoint.

Please note that not all ticket types are available for every booking. The booking response provides the information which ticket types are downloadable.

4.6. Timeouts and Retry

Calls to the prebooking and payment API will typically complete in less than a second, and booking calls in a few seconds. However, for bookings with many prebookings or in case of service impediments, calls may take longer and clients should be prepared to handle this. A successful booking call may take up to 50 seconds.

Even if an API call is aborted due to a client-side or a gateway timeout, the B2P backend may still complete processing the call successfully. The client should retry the API call after a timeout to get the result of the original call. If the call is repeated with the same request headers, arguments and body, the server will return the result from the completed original call instead of processing it anew.

If the original call is still being processed by the backend at the time of the retry, the call will return an already-processing problem. In this case, the client should wait a few seconds and try again. Once processing is complete, the retried call will return the result. Clients may retry multiple times, until either the result or an error other than an already-processing problem or a timeout is returned.

The already-processing problem returns an HTTP 202 Accepted response, which is in the range of 20x success responses. The response body however contains a problem json document instead of the json specified in the B2P API for the usual success response. API clients should be prepared to treat HTTP 202 responses separately from other success responses.

If an API call to prebooking, payment or booking results in an error other than a timeout, clients may also retry the call. The backend will process the call anew. However, the chance of a successful retry depends on the nature on the error. If the error occurs again, clients should start a new booking process instead of retrying.

4.7. Additional services

Master data

Networks

The B2P web service supports the following services to query information about Swiss railway networks.

  • /api/networks allows to fetch the list of available swiss railway networks. The response contains a list of the available networks including a link to query the network details.

  • /api/networks/{networkId} allows to fetch the network details for a given network. The response contains the name and description of the network and a list of the network zones.

  • /api/networks/{networkId}/zones allows to fetch the list of network zones for a given network. Please refer to the network zone map of the railway company to locate the zone geographically.

See 20 networks-controller for details.

Products

The B2P web service supports the following services to query information about offered products.

  • /api/products allows to fetch a complete list of supported products.

  • /api/products/{productId} allows to fetch product details for a given productId. These details include the available customer segments for a product as well as the offer-entrypoints that may be used.

See 20 products-controller for details about the returned data.

Journey services

Locations

The location service is used to perform a pattern-based search to retrieve a list of matching locations of type STATION in the journey planner database. The result is a possible match of (train-, bus-, tramway-) stations with corresponding UIC codes (standard ID-type for Switzerland) and coordinates.

To get an initial list of all possible stations please visit the public transport page https://opentransportdata.swiss/en/organization/oevch and download the newest Timetable xxxx (GTFS) zip file. Unpack it and use the stops.txt CSV file. Inside the file you can ignore the stops, where a parent station is given and import the parent station itself with the stop_id but without the trailing P. Further you need to remove the possible duplicated entries for the stop_id. Be aware that the stop list will be updated +/- weekly. So it is suggested to updated the initial stop list on a regular base.

See 20 locations-controller for details.

Trips

The trips service returns different trips (concrete journeys from the viewpoint of a passenger planning his trip) for a specified origin and destination (and via). The response body of the trip request contains some navigation links indicating the next services you can call. All journeys rely on a yearly plan (usually changing in first decade of December). Each Segment means a separate transport-product (by means passengers need to change at the ending Stop of a Leg). The underlying system provides a default change time when passengers need to switch transport-products. The underlying system handles date/time requests in Swiss timezone "Europe/Zurich" (seconds are irrelevant). If you’re interested in real-time data, you can find additional information here.

See 20 trips-controller for details.

Prices

The B2P web service prices operation allows to query the cheapest prices for a given trip.

Use the trips service to retrieve the (required) tripId.

A price offer cannot be used for prebooking and indicates the best price only.

By default the service assumes that the traveler is an adult with half-fare reduction card. The price offer can be based on a regular fare or (if available) a super saver fare. Starting from V2 the traveler details may be specified when requesting prices to overwrite the defaults. It is possible to hand over a desired product id to get prices for only this product.

See 20 prices-controller for details.

There may be multiple tripIds specified in one request to fetch multiple price quotes at once. If at least one of the prices cannot be determined, a price-not-determinable is returned.

To avoid breaking the look-to-book ratio between offers and bookings, we recommend using this services for requesting simple price information and to only use the trip offers service after the user chose a specific trip.

Product-Prices

The B2P web service product-prices operation allows to query the cheapest prices for a given product at a given date range with the given passengers.

A price offer cannot be used for prebooking and indicates the best price only.

The number of available products at the given price is also returned. There may be more instances available at a higher price.

See 20 prices-controller for details.

Barcodes

The service is used to generate the barcode for the public transport platform (German "öV-Platform"), which is part of the interface NOVA Control (German "NOVA Kontrolle"). This service allows authorized callers for producing barcodes (2d-codes and textcode). These barcodes can be validated by control devices using one of the control applications of the öV-CH (either the "KoServ app" running on Android or one of the "KoServ kernels" (or KSK)). The barcode produced are conform to the e-Ticketing concept.

The barcode service generates a barcode from a collection of attributes which are passed by the caller. The attributes have a type and a semantic attached to them. Most of the attributes are optional. The barcode is generated on the base of the attributes which are effectively passed. The validation of the barcode by the control application exploits the semantic of each attribute in order to compute the validity of the ticket.

A barcode can be deleted using its barcodeId, which cancels it in KoServ and invalidates it for subsequent controls.

See 20 barcodes-controller for details.

5. After sales process

In some situations, the booked ticket has to be refunded or cancelled for various reasons (technical errors, sickness, wrong ticket, change of plans or others). To address this, the B2P web service offers an aftersales functionality.

The aftersales flow (often referred as a SAV process, French "service après-vente") is shown in the figure below.

Aftersales Process Diagram
Figure 2. Aftersales process

5.1. Booking data

The booking data services provide information about the booking. The service is general usable to retrieve information about the booking and its tickets.

The booking data can be retrieved via

ticket id

Retrieve booking data by entering the id of the ticket (printed on the ticket).

or booking id

If the booking id is known from the sales process, it can be used to retrieve the booking data.

See version 2 services 20 bookings-controller for details.

5.2. Refund data

The refund data service provides information about the refund possibilities and the reasons. A refund reason is necessary to get an offer for a refund.

It can be retrieved via booking id.

5.3. Refund offers

To get an offer for a possible refund, the refund reason must be selected, as the possibilities are returned in the B2P refund-data service. If no possible reason is returned in the booking data service, no refund offer and so no refund will be possible.

B2P refund offers are valid for 20 minutes only. After that the refund offers are discarded.
Not all products can be refunded and rules apply on partially used products and retention.

Currently there is only one refund reason possible over B2P. This is set by default.

refundReasonId description

NICHT_BENUTZT

Not used. Can only be used before start of validity of the ticket.

To call the refund offer service it is necessary to hand in all ticket ids, where a refund offer should be created. If one given ticket is not refundable, an error will be returned. If all given tickets are refundable for the same refund reason, one refund offer including refund offer id will be returned. This contains the compelete information of refundable amount, original price, excess, used part and so on.

See 20 refund-offers-controller for details.

5.4. Refund

The refund service confirms the refund of the tickets included in the given refund offer. It requires the refundOfferIds as input which are returned in the Refund offers response. See B2P refund-offers service. Only if the refund of all tickets of all given refund offers can be successfully refunded, the service will return a success response. Else an error will be returned and none of the tickets in one of the given refund offers will be refunded.

On success it returns a savBookingId and a savTicketId. For the moment this information is not necessary to be stored. Instead keep the bookingId and call the B2P booking-data service to check the new status of the tickets.

The refunded amount is always put back on the used payment method. So if the payment has been done with credit card, the refunded amount will be credited to the same credit card. In case of invoice payment, the refunded amount will be credited on the next invoice.

See the POST service 20 refund-booking-controller for details.

6. Error handling

The B2P web service implements the RFC 7807 (https://tools.ietf.org/html/rfc7807) standard.

6.1. Format

With this besides a HTTP code, a problem json body with further information is returned.

HTTP header

The response header in an error case is always like the following:

content-Type: application/problem+json

The following HTTP codes are used:

HTTP code Name Description

202

Accepted

Processing of this request was already started by another call.

204

No Content

The server successfully processed the request, and is not returning any content.

400

Bad Request

Request contains errors / is malformed.

401

Unauthorized

Authorization is required.

403

Forbidden

The requested action is forbidden for the current authorized user.

404

Not Found

Requested entity could not be found

406

Not Acceptable

The requested accept header in the request cannot be accepted.

409

Conflict

Indicates that the request could not be processed because of conflict in the current state of the resource

For server-side problems, the usual 50x error codes are used.

Payload Structure

The basic structure is defined by RFC 7807 (https://tools.ietf.org/html/rfc7807). The B2P web service leverages the extensibility of the RFC and provides a JSON response body with the following structure:

{
 "type": "./b2p/{service-name}/{problem-id}",
 "title": "{title}",
 "documentation": "https://b2p.app.sbb.ch/docs/index.html#error-handling",
 "status": {code},
 "detail": {detail for B2P developers},
 "instance": "/api/{service-name}",
 "displayMessages": [
     "{end-user message 1}",
     "{end-user message 2}",
  ]
}

The table below explains all B2P relevant attributes.

Attribute Required Description

type

required

The problem type identifier, as per specification an URN.

title

optional

A short explanation of the current problem for B2P developers. Language: English. Mostly static values as listed below in section . Do not share the title with end users.

documentation

optional

An RFC 7807 extension to refer to the official B2P cookbook (basically the present document).

status

optional

An integer value representing the HTTP Status code.

detail

optional

More detail for B2P developers regarding the current problem. Do not share the message with end users.

instance

optional

The path to B2P web service endpoint that emitted the problem.

displayMessages

optional

An RFC 7807 extension to provide a list of translated end user messages. Language according to HTTP Header 'Accept-Language'.

Example

Here an example for the locations service.

HTTP/1.1 400 Conflict
Content-Type: application/problem+json Content-Language: en

{
 "type": "./b2p/locations/name-too-short",
 "title": "The given name parameter is too short to do a reasonable locations search.",
 "documentation": "https://b2p.app.sbb.ch/docs/index.html#error-handling"
 "status": 400
}

6.2. Reference

This section is to list all errors ("problems") that B2P client developers may expect.

Commonly Used Problem Definitions

Certain problem types are thrown by multiple services. In this case, the service name in the reported problem indicates which of the services has reported the problem. These common problems are summarized in the following. The last column indicates which services can report the according problem.

HTTP code

problem-id

title

services

202

already-processing

A request to the service with the same parameters has already been made and is still being processed. Retry later to get the result after processing is finished.

bookings, payment

204

no-offer-found

No offer could be found for the requested passenger(s)/products.

400

request-parameter-invalid

A request parameter or a combination of input values is invalid.

all

400

offer-request-too-far-in-the-future

The offer request’s journey date is too far in the future. Refine the journey date related parameters (trip-offers: tripId/returnTripId route-offers: validFromDate/validFromTime, product-offers: date/time).

400

offer-request-too-far-in-the-past

The offer request’s journey date is too far in the past. Refine the journey date related parameters (trip-offers: tripId/returnTripId route-offers: validFromDate/validFromTime, product-offers: date/time).

403

access-to-booking-id-forbidden

You are not allowed to access the given booking with the given contract.

bookings, booking-data

403

product-not-activated

At least one of the products is not activated on this contract-Id.

(group-)product-offers, (group-)trip-offers, route-offers, prebookings, bookings

403

contract-not-found

The given contract-Id is invalid.

(group-)trip-offers, (group-)product-offers, route-offers

403

client-Id-not-linked-to-contract-Id

Credentials do not match. Check your user-Id and contract-Id.

(group-)trip-offers, (group-)product-offers, route-offers

404

ticket-not-found

The ticket with the given id could not be found or does not belong to the given booking id.

bookings, booking-data

404

no-timetable-search-result

The timetable search yielded no result.

trips, locations

406

ticket-format-not-supported

The requested ticket format is not supported for the given ticket.

bookings, booking-data

409

illegal-reuse-conversationId

The conversation-id must not be reused as it is already in a final state.

trips, trip-offers, prebookings

500

unknown-booking-system-error

The booking system aborted the operation due to an unknown error.

prebookings, bookings

500

request-processing-aborted

Processing of the request was aborted because it took too long to complete. This may be caused by a temporary service impediment.

Locations

service-name = locations
HTTP code problem-id title

400

name-too-short

The given name parameter is too short to do a reasonable locations search.

Trips

service-name = trips

Please also take into account the common problems, as the trips service also reports problems defined there.

HTTP code problem-id title

400

train-type-invalid

At least one of the given train types is invalid.

Prices

service-name = prices
HTTP code problem-id title

404

price-not-determinable

The price cannot be determined. Reasons for this can be e.g. that the passed trip id is invalid, that the trip id is too far in the past or too far in the future, or that the calculation timed out.

Trip-Offers

service-name = trip-offers

Please also take into account the common problems, as the trip-offers service also reports problems defined there.

HTTP code problem-id title

404

trip-id-too-far-in-the-future

The trip id parameter or the return trip id parameter is too far in the future. In general it is possible to get an offer for the next 2 months.

404

trip-id-too-far-in-the-past

The trip id parameter or the return trip id parameter is too far in the past. It is not possible to get offers for this trip.

400

trip-id-invalid

The trip id is invalid.

Group-Trip-Offers

service-name = group-trip-offers

The Group-Trip-Offers service uses the same problems as the Trip Offers service.

Prebookings

service-name = prebookings
POST Prebookings
HTTP code problem-id title

404

offer-id-not-found

At least for one offer id the according offer could not be found. The reason for this is that either the offer id expired and is no longer valid or that it never existed and is, thus, invalid.

400

sales-parameter-invalid

At least one given sales parameter is unknown or contains an invalid value.

400

sales-parameter-required

At least one required sales parameter was not given in the request.

400

birthdate-does-not-match-age-from-offer

At least one given passenger has a birthdate that does not match the provided age from the offer

400

saver-fare-quota-exceeded

The maximum quota of the saver fare level was been exceeded. This information is known earliest on prebooking, so offers will still be returned from service, although not bookable.

400

reservation-preference-rejected

At least one reservation preference is not available (anymore).

Bookings

service-name = bookings

Please also take into account the common problems, as the bookings service also reports problems defined there.

POST Bookings
HTTP code problem-id title

400

too-many-prebooking-ids

The limit of prebookingIds per booking had been exceeded.

404

prebooking-not-found

At least one of the given prebooking ids could not be found. This problem occurs if either the according prebooking id has expired and is no longer valid or if it has not been returned by a previous call of the prebooking service.

404

passenger-ids-of-prebookings-and-offers-not-matching

There has been at least one passenger id for the prebooking which does not match with a passenger id for the offer.

404

payment-confirmation-missing

The confirmation that the payment has been successfully done is missing. Please take care that the same x-conversation-id is used in payment process and in bookings.

Booking data

service-name = booking-data

Please also take into account the common problems, as the booking data service also reports problems defined there.

HTTP code problem-id title

400

legId-required-for-ticket-download

The ticket download of reservation bookings requires the legId request parameter.

404

booking-not-found

The booking with the given id could not be found. This problem can occur if the booking with the given id does not exist at all. Besides it can occur if there is a general problem with the processing of the booking.

403

access-to-booking-id-forbidden

You are not allowed to access the given booking with the given contract.

404

ticket-not-found

The ticket with the given id could not be found or does not belong to the given booking id.

406

ticket-format-not-supported

The requested ticket format is not supported for the given ticket.

Refund-Offers

service-name = refund-offers
HTTP code problem-id title

404

ticket-not-found

At least for one of the given ids, no ticket could be found.

404

ticket-already-compensated

The product cannot be refunded because a reimbursement has already been issued.

404

ticket-already-refunded

At least one ticket has already been refunded.

404

refund-not-possible

At least one ticket could not be refunded with the given refund reason. At least one ticket is not refundable. The reason could be that the ticket is not refundable or that the given refund reason is not supported.

Refund Booking

service-name = refunds
POST Refunds
HTTP code problem-id title

404

refund-offer-not-found

At least one of the given refund offers could not be found. This problem occurs if either at least one of the given refund offers has expired and is no longer valid or if at least one refund offer id has not been returned by a previous call of the refund-offers service.

7. Status Information

7.1. Service Status

You can check if the B2P service is currently available by calling the 20 status service. The response contains the overall status of the B2P service as well as the individual statuses of its component.

OK means that the service/components are operating normally. ERROR means that a component is not available or only partially available, or that its functionality is impeded. The overall status shows ERROR if at least one component is in status ERROR.

Note that calls to the B2P API and even whole sales processes may succeed even if the status service shows an error. This is because the B2P API functions you use might not be affected by the partial outage of a component.

The service status response also contains the version of the individual components. This is only informational and not relevant for using the B2P API.

7.2. Service Info

The 20 service info service returns announcements about the service operation. This is used for example to announce planned maintenances in advance.

7.3. B2P API Status Monitor

The information returned by the status and info services is also available on the B2P API Status Monitor web page.