Skip to content
grotmoll edited this page May 20, 2025 · 18 revisions

Purpose

The purpose of OMSA is to enable simple integrations between different public transport booking and reservation systems. OMSA was designed to enable actors to utilize open data from the National Access Points, to create value for themselves and for the travellers. OMSA aspires to be a tool for actors that want to collaborate on booking and reservation, and thereby lower the costs integration across the sector. OMSA is based on the premise of a distributed eco-system, where existing services, solutions, components and standards are re-used and re-purposed to create value for the players in the industry. Under the premise of "no need to re-invent the wheel", OMSA aims to add the value of collaboration and integration without the need for sizeable up-front investments.

Main objectives

  • Financial: reduced operational costs, easy to implement, early return on investment
  • Ease of use: global known meta standards, simple API, highly structured
  • Steady, future-proof, modern technologies, compliance with many standards
  • Enable true multimodal travel, which is the goal to strive for
  • Return On Investment - structured & understandable
  • Reuse (e.g. NeTEx, OTP outcomes)

Example use case

In this use case, we describe how a traveller searches for travel options at the operator 4W, adds some personal details (because 4W does not allow anonymous travel) and purchases (books) the selected travel option. Btw, 4W is a bus operator, but it could have been any operator.

Search for offers

The traveller uses a reseller, who uses a trip planner. The trip planner requests travel options at 4W, one of the legs of the trip can be executed by 4W.

Request

Reseller calls: https://4W.mobility/OMSA/v1/processes/search-offers/execute

{ "inputs": {
    "type": "SEARCH_OFFER",
    "timestamp": "2025-05-14T12:55:17.170Z",
    "specification": {
      "from": { "placeId": "JL:StopPoint:349-34123" }
      "to": { "placeId": "GPS:4.43243,51.3492340" },
      "startTime": "2025-05-14T12:55:17.170Z"
    },
    "travellers": [
      { "type": "user_profile",
        "requirements": [ { "type": "service", "class": "FIRST_CLASS" } ],
        "id": "9f7f69ec-d68c-4751-8ec4-fd059f820cb8",
        "ageGroup": "ADULT" }
    ]
  }
}

Some key elements to highlight in this request:

place references
The `placeId` can be a direct reference to an external data source (i.e., data reuse). To support this, there's a dedicated endpoint: `/collections/datasources/items`.
This endpoint describes how to resolve external IDs, such as those starting with `"JL:StopPoint"`. These typically refer to entries in published NeTEx datasets.
Transmodel concepts
Terms like `"type: service"`, `"age group"`, and `"travel specification"` map directly to concepts and attributes defined in the Transmodel conceptual framework.
Requirements list
The list of requirements can be extensive — including, for example:
- Luggage
- PRM (Persons with Reduced Mobility) needs
- Bicycles
- Specific `ServiceJourney`
- A particular asset (e.g., a designated bike or seat)

In this simplified example, we've only added a single requirement: first-class service.

👉 If you're interested in the full set of information that can be passed when searching for offers, check out: Search offer request.

Response

4W can respond with a collection of offers (just one shown):

{
  "type": "OfferCollection",
  "offers": [
    { "id": "5232f7f8-bfab-4330-a8ad-0eecefa3921f",
      "type": "offer",
      "properties": {
        "legs": [...],
        "products": [ { "type": "product",
                        "productId": "FIRST_CLASS_TICKET",
                        "productName": "First class single trip ticket",
                        "guarantees": [ { "id": "PASSENGER_SUPPORT", "type": "guarantee" } ] } ],
        "price": { "amount": 3.62, "currencyCode": "EUR" },
        "summary": { "refundable": true },
        "expiryTime": "2025-05-13T12:55:17.193Z"
      },
      "links": [
        {
          "rel": "select-offers",
          "description": "Select this offer",
          "method": "POST",
          "href": "/v1/processes/select-offers/execute",
          "type": "application/json",
          "body": {
              "inputs": { "type": "select_offers",
                          "timestamp": "2025-05-14T13:23:25.004Z",
                          "offerIds": [ "5232f7f8-bfab-4330-a8ad-0eecefa3921f" ] }
          }
        }
      ]
    }
  ],
  "numberMatched": 1,
  "numberReturned": 1,
  "links": []
}

Some key elements:

Consistent response structure
The response follows a fixed structure that includes:
- `type`
- `offers` (in other cases, this might be named `ancillaries` or some other plural name)
- `properties`
- `links`

This structure is reused across all responses, ensuring consistency.
Use of external data
External references are used again here.
For example, a `traveller ID` can also be an external identifier — such as a reference to an eIDAS 2.0 wallet or an account-based identity.
Link-driven process flow
The `links` object describes possible next steps.
In this case, the offer can be selected, resulting in a package (a SALES OFFER PACKAGE in Transmodel terminology) that can be purchased afterwards.
This approach allows operators to explicitly define the flow of their implementation — very convenient.
Explicit type declarations
Object types are often explicitly stated in the response to improve human readability.
Built-in paging support
For the technical audience: pagination is natively supported using properties like:
- `numberMatched`
- `numberReturned`
- links { `rel`: `next` }
- and links { `rel`: `prev` }

Clients can control pagination using OGCs default query parameters like `limit` and `offset`.
OGC APIs
This structured response pattern and process control are made possible through the use of OGC APIs.

This API specifically implements:
- OGC API - Records
- OGC API - Processes
- (and a minimal use of Features)

State transition diagram

This STD shows the general process flows from OMSA. Most transitions correspond with an endpoint in the OMSA specification.

State Transition Diagram

Transitions

Module Transition Endpoint Description
➡️ Offers
search offers /processes/search-offers/execute search for offer(s)
➡️ Pre-sales
select offers /processes/select-offers/execute select the offer(s) into a single package, ready to modify or to purchase
update traveller /processes/update-traveller/execute update traveller (name, cards, licenses etc)
remove traveller /processes/update-traveller/execute update traveller (name, cards, licenses etc)
➡️ Purchase
purchase offer not implemented (yet) directly purchase an offer, without configuring it
purchase product not implemented (yet) directly purchase a ticket, or use a shared bike
purchase package /processes/purchase-package/execute results in a pending package (not confirmed)
release package /processes/release-package/execute release the selected offers in the package
confirm package /processes/confirm-package/execute confirmation of the purchase
rollback package /processes/rollback-package/execute the pending purchase is rolled back
➡️ After sales
extend expiry time /processes/extend-expiry-time/execute extend the purchase window
claim refund option /processes/claim-refund-option/execute claim one of the refund options
confirm refund option /processes/confirm-refund-option/execute confirm the option to refund

Pattern: /processes/{operation}-{subject}/execute

As you can see, all endpoints have the same structure: 'processes', followed by an operation on a subject (like 'search' for 'offers') and 'execute'. This is result of the compliance with OGC API Processes. This meta-standard has a lot more advantages, like a standardized way to publish meta data of the complete implementation.

Data sets

To support the transitions, there is also data required:

Dataset Endpoint Description
Datasources /collections/datasources/items Specification of external datasources (like NeTEx)
Ancillaries /collections/ancillaries/items Applicable ancillaries for a package (set of offers)
Assets /collection/ancillaries/items Applicable assets for a leg (like seats, or bikes)
Travel documents /collections/travel-documents/items References to tickets, and ways to open shared modes
Refund options /collections/refund-options/items Refund options applicable for a travel, offer, ticket, ...

Pattern: /collections/{collection name}/items

And again, a fixed structure: 'collections', followed by a collection name and 'items'. This time to comply with OGC API Records (and in case of ancillaries, even with OGC API Features, that returns geoJSON).

FAQ

Is this usable for zonal tickets?
Absolutely. It is possible to deliver offers with only products, like zonal tickets. Even in the offer request, you can already specify the zones (if you have this information already up forehand).
Is it possible to use this for 'swipe-in-swipe-out' scenario?
Not yet, but due to the structure of the API (complying with OGC APIs), it is not hard to extend it. A /processes/swipe-in-product/execute or something like that can be arranged.
Relying on existing (local) data sources makes it hard to resell e.g. abroad. How would you address this?
OMSA focusses on standardizing the process, not the products. This opens up possibilities to migrate from existing, often peer-2-peer solutions to more commonly accepted specification, without breaking the bank.
We don't use a 2-step purchase process. Can OMSA facilitate a purchase, delivering directly a confirmed purchase?
It can, but we don't want to make a lot of dialects. Therefore, we prescribe to return a PENDING package, but in the links, you can specify that the 'confirm-package' link is not mandatory. This implies that when the rollback expiry time has expired, the package will be confirmed automatically. When the 'confirm-package' link is mandatory to execute, it implies that an explicit confirm is required, otherwise the pending package will expire.

Clone this wiki locally