NAV

MOIA API

API Endpoint

https://api.moia.io

Endpoint for Testing

https://api-sandbox.moia.io

MOIA provides an API to easily integrate our services with other apps. This pages guides you through our endpoints and how to use them.

Public Endpoints

The following endpoints are public and can be used by anyone. They are, however, throttled. If you plan to use them for production purposes, please contact us for an API Key.

Closed Endpoints

The following endpoints are for partners who use our service with their own user pool:

The API is organized around REST. Our API has predictable, resource-oriented URLs, and uses HTTP response codes to indicate API errors. We use built-in HTTP features, like HTTP media types and HTTP verbs, which are understood by off-the-shelf HTTP clients.

Most endpoints support Protobuf and JSON as content types. You can find the current protobuf files at github.com/moia-dev/api.

Each resource has its own release cycle and can therefore have its own content-type-version.

Change Log

Changes to this document will be listed here.

Date Change
2018-12-11 Update Error Codes.
2019-07-01 Update /serviceareas to Version 2.
2019-07-01 Remove /trip/inquiry endpoint.
2019-07-01 Add Events v2, deprecate v1.
2019-07-10 Update Error Codes.
2019-07-11 Update /offer response: fix offerSignature, add priceMetadata, add destination.
2019-07-12 Add docs for /trip/last endpoint.
2019-07-16 Remove traceId from events.
2019-07-22 Remove paymentNonce and couponInformation (internal APIs).
2019-09-20 Add documentation for the DIRECT_WALK_IS_FASTER error code.
2019-09-20 Remove documentation for the deprecated feedback error codes.
2019-11-18 Added estimate-endpoint.
2019-12-10 Add estimatedVehicleArrival to estimate-endpoint response.
2019-12-10 Move error codes into their respective endpoint documentation and add errors to estimate-endpoint.
2020-01-13 Add estimateId to estimate-endpoint response.
2020-01-22 Add etaAvg to estimate-endpoint response.
2020-03-02 Add retry behavior of payment related events and improve description for the fields of payment related events.
2020-05-25 Add TRIP_COMMITTED, TRIP_REJECTED, PICKUP_STOP_FIXED, DELIVERY_STOP_FIXED and VEHICLE_FIXED event preview.
2020-05-29 Add preview of v6 of /trip/ endpoints.
2020-06-23 Restructure /offer response and /order input.
2020-07-06 Add stop.walkingDuration and stop.walkingDistance to PICKUP_STOP_FIXED and DELIVERY_STOP_FIXED.
2020-07-06 Add CUSTOMER_NOT_SHOWN and SERVICE_ABORTED events that are to replace TRIP_CANCELED_BY_HUB and TRIP_CANCELED_CUSTOMER_NOT_SHOWN respectively.
2020-07-15 Fix the supported json media type for calls to the /trip endpoint.
2020-08-13 Add offers.id, change tripRequest.walkingDuration, rename guaranteedPrice to signedPrice, rename selectedOffer to offer and updated error codes for v6 /trip/offer and /trip/order.
2020-08-14 Add error codes to /cancel endpoint for v6.
2020-09-23 Add OrderTripError.Code.CODE_DISPATCHING_ERROR and remove /trip/last endpoint for v6.
2020-09-29 Remove CODE_SAME_PICKUP_AND_DELIVERY_STOP from TripRequest errors for v6.
2020-10-09 Rename CODE_DIRECT_WALK_IS_FASTER to CODE_WALK_IS_FASTER from TripRequest errors for v6.
2020-10-29 Remove /trip/last endpoint.
2020-12-04 Add CODE_KILL_SWITCH_ACTIVE to errors for requests to trip/offer and trip/order.
2021-02-09 Remove v5 of /trip/ endpoints.
2021-02-10 Add v7 of /trip/offer/ and /trip/order/ endpoints.
2021-03-25 Remove v6 of /trip/offer/ and /trip/order/ endpoints.
2021-04-20 Add v1 of pickup-estimate endpoint.
2022-04-29 Remove TRIP_CANCELED_BY_HUB and TRIP_CANCELED_CUSTOMER_NOT_SHOWN events.
2022-09-20 Enhance documentation for estimate, /trip/offer/ and /trip/order/ for wheelchair support.
2023-06-15 Refine wording for TRIP_COMMITTED and VEHICLE_FIXED event description.
2023-10-13 Add price parts to offers. Add offer metadata field. Deprecate price metadata.
2023-11-16 Add a description field to the price parts.
2024-02-09 Deprecate disjoint offer lists in favour of the new allOffers in /trip/offer. Add Express offers.

Media Types

The following media types are supported by all endpoints (with varying versions):

The /trip endpoint additionally supports

It is recommended to declare one of the supported media types as part of the Accept header. The response body will be returned accordingly. If no Accept header is specified, the latest Protobuf version will be returned. More information about versioning is described in the section Versioning.

If the HTTP request contains payload in the body, the Content-Type header needs to be set as well. Otherwise, a 415 Unsupported Media Type is returned.

Versioning

To enable a strong versioning scheme for HTTP, MOIA uses vendor-specific media types. A good explanation of this can be found here.

With a media type, the client can query a specific version of the content from a server. It defines the expected major version via the Accept header. If the media type does not include a version, e.g. application/json, the latest major version is assumed.

MOIA uses a major.minor version concept which is best explained in the following example.

Example Request: Version 1.0

curl https://api.moia.io/customer \
  -H "accept: application/vnd.moia.v1+json"

Example Response: Version 1.0

{
  "id": "123",
  "email": "max@mustermann.de",
  "name": "Max Mustermann"
}

Let’s assume an HTTP endpoint to retrieve customer information. The endpoint in version 1.0 has declared the fields id, email and name.

On the right you can see that only the major version is specified in the Accept header.

Now, let’s assume the server updates the version to 1.1 by adding the field phones. The Accept header application/vnd.moia.v1+json will now return the new field phones as well:

Example Response: Version 1.1

{
  "id": "123",
  "email": "max@mustermann.de",
  "name": "Max Mustermann",
  "phones": [
    {
      "number": "+12345",
      "type": "Mobile"
    }
  ]
}

In other words, the Accept header specifies the major version of the response, not the minor version. The server will always return the latest minor version. Minor version are compatible with each other. In our example, if the client only understands version 1.0 and the server replies with version 1.1, the client parser will ignore the phones field.

Versioning support

The design has some important consequences. The client that uses the MOIA API:

This means that MOIA will support multiple major versions. Once a version is introduced, it will be supported for 6 months.

Errors

MOIA uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided, e.g. a required parameter was omitted. Codes in the 5xx range indicate an error with MOIA’s servers.

Dispatching Error (status code 400)

{
  "code": "DISPATCHING_ERROR",
  "message": "We failed to find a vehicle to fulfil the offer or order request."
}

Malformed request (status code 400)

The request content was malformed:
Entity could not be unmarshalled.

Unauthorized request (status code 401)

{
  "message": "Unauthorized"
}

Resource not found (status code 404)

The requested resource could not be found.

Missing field in request (status code 422)

{
  "errors": [
    {
      "path": "/destination",
      "message": "Value is required."
    }
  ]
}

Unsupported Media Type (status code 415)

The request's Content-Type is not supported. Expected:
application/vnd.moia.v5.0+json or application/json or
application/vnd.moia+x-protobuf or application/x-protobuf or
application/vnd.moia+json or application/vnd.moia.v5.0+x-protobuf
Code Description
200 OK Everything worked as expected.
400 BadRequest The request is malformed or cannot be fulfilled, e.g.
- no vehicle is available,
- a service area cannot be resolved,
- an offer expired.
401 Unauthorized The Moia-Auth header is empty or the provided API key is not valid.
404 Not Found The requested resource doesn’t exist.
415 Unsupported Media Type The Content-Type for the request payload is either not set or not supported.
422 Unprocessable Entity The server cannot process the request due to an apparent client error (e.g., field is missing in the request, a field value is negative but only positive numbers are allowed).
429 Too Many Requests Too many requests hit the API too quickly. Retry again with a back-off according to the Retry-After response header.
503 Service Unavailable The service is not available for the short time. Retry again with a back-off according to the Retry-After response header if present or exponentially otherwise.
500, 502, 504 Server Errors Something went wrong on MOIA’s end.

The payload is formatted differently for different errors.

400 BadRequest errors either indicate a malformed payload (see second example) or include an error code, e.g. DISPATCHING_ERROR, so that these errors can be handled programmatically. For a list of possible error codes, see the chapters for specific endpoints below.

401 Unauthorized errors include a message.

422 Unprocessable Entity errors include an error list. Each error object contains a path and message to indicate which field in the request body could not be processed. Note that this list might not be exhaustive.

Error Codes

We try to return helpful error codes that can be handled programmatically. Every route lists its possible error codes in the respective documentation.

/estimate

POST https://api.moia.io/estimate

Supported Versions

application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v1+x-protobuf

application/json
application/vnd.moia+json
application/vnd.moia.v1+json

This endpoint takes two coordinates, origin and destination, and returns the estimated range of minutes it takes a MOIA to go from origin to destination and the estimated vehicle arrival time. An optional boolean parameter wheelchairAccessible can be set to true if estimates are for wheelchair accessible vehicles. It also returns an estimation for the current price and a link which will let the user open the MOIA app with prefilled coordinates. If the MOIA app is not installed, the user will be led to the Apple App Store or Google Play Store respectively. If you use an API Key, the link contains information to track the conversion from your service.

Request

Example Request

curl --request POST \
  --url https://api.moia.io/estimate/ \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --header 'x-api-key: public' \
  --data '{
  "origin": {
    "lat": 53.549576,
    "lon": 9.962196
  },
  "destination": {
    "lat": 53.552679,
    "lon": 10.006687
  },
  "wheelchairAccessible": false
}'
Field Name Description
origin
required
The starting point, as latitude and longitude.
destination
required
The destination, as latitude and longitude.
wheelchairAccessible
optional
Boolean indicating if the estimate is for a wheelchair accesible vehicle. Default: false

Response

Example Response

{
  "etaMin": 16,
  "etaAvg": 20,
  "etaMax": 25,
  "estimatedVehicleArrival": 5,
  "price": {
    "amountMin": "5.00",
    "amountMax": "6.00",
    "currency": "EUR"
  },
  "deepLink": "https://www.moia.io/de-DE/app/tripPreferences?id=public_6a0ade94-e4db-4f2e-913e-bf90b9988565&origin_lat=53.549576&origin_lng=9.962196&dest_lat=53.552679&dest_lng=10.006687",
  "estimateId": "public_6a0ade94-e4db-4f2e-913e-bf90b9988565"
}
Field Name Description
etaMin / etaMax Minimum / maximum amount of minutes until delivery, starting from now.
etaAvg A weighted average of the etaMin and etaMax. Showing etaMin and etaMax is strongly preferred!
estimatedVehicleArrival Estimated amount of minutes until vehicle arrival, starting from now.
price Lower and upper bound for the current price for this trip, rounded to full euros.
deepLink A link to open the prefilled MOIA app on iOS or Android.
estimateId Unique identifier of the estimate.

Errors

See Errors for the general error strategy of the api. The following types of error codes can be returned from this route.

Error Code Description
GENERAL_ERROR An internal, technical error occurred.
NOT_WITHIN_SERVICE_AREA The requested location is not inside a known service area.
NOT_WITHIN_SERVICE_HOURS The requested ride time is not within the service hours.
WHEELCHAIR_ACCESSIBLE_SERVICE_NOT_AVAILABLE A wheelchair accessible service is not available in this service area.

API Key

Note that while this endpoint is public, it is throttled. If you intend to use it for production use cases, please contact us for an API Key to send as x-api-key header.

Response time

You can expect an average response time of 200ms, and peaks of up to 400ms for some requests.

Example

We have provided an example application at https://moia-dev.github.io/trip-estimate-example/. You can find the code at https://github.com/moia-dev/trip-estimate-example.

/pickup-estimate

POST https://api.moia.io/pickup-estimate

Supported Versions

application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v1+x-protobuf

application/json
application/vnd.moia+json
application/vnd.moia.v1+json

This endpoint takes a coordinate pair, lat and lon, of a customer location and returns the estimated number of minutes until a customer can be picked up.

Request

Example Request

curl --request POST \
  --url https://api.moia.io/pickup-estimate/ \
  --header 'accept: application/json' \
  --header 'content-type: application/json' \
  --header 'x-api-key: public' \
  --data '{
      "lat": 53.549576,
      "lon": 9.962196
    }'
Field Name Description
lat
required
The latitude of the starting location.
lon
required
The longitude of the starting location.

Response

Example Response

{
  "minutes": 5
}
Field Name Description
minutes Estimated number of minutes until the pickup.

Errors

See Errors for the general error strategy of the api. The following types of error codes can be returned from this route.

Error Code Description
GENERAL_ERROR An internal, technical error occurred.
NOT_WITHIN_SERVICE_AREA The requested location is not inside a known service area.
NOT_WITHIN_SERVICE_HOURS The request is not within the service hours.

API Key

Note that while this endpoint is public, it is throttled. If you intend to use it for production use cases, please contact us for an API Key to send as x-api-key header.

Response time

You can expect an average response time of 200ms, and peaks of up to 400ms for some requests.

/auth

Supported Versions

application/json
application/vnd.moia+json
application/vnd.moia.v1+json

All calls to our API require a valid token in the Moia-Auth header. There are two kinds of tokens: general client_credentials-tokens which allow actions such as

and customer-specific tokens which can be used to

To receive a token, you need to send a POST request to https://api.moia.io/auth/token that includes your Partner API credentials as HTTP basic access authorization header and the requested grant_type as payload.

All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

To retrieve partner API credentials please contact us. Do not share these credentials in publicly accessible areas such GitHub, client-side code, and so forth.

Authenticating as Client

POST https://api.moia.io/auth/token

A client can authenticate itself with the client_credentials grant. In this case no specific user is involved, but the client acts in a global context. This is mainly used for account/customer management actions.

In short, the client exchanges its client id and client secret for a token. An id token is used in our API for this flow. This id token must be sent along in the Moia-Auth header when using APIs in a global context.

The request must contain an HTTP basic access authorization header, with the client id as username and the client secret as password.

Request

Example Request

curl https://api.moia.io/auth/token \
  -X POST \
  -H "accept: application/vnd.moia+json" \
  -H "content-type: application/vnd.moia+json" \
  -u 'client-id:secret' \
  -d '
  {
    "grant_type": "client_credentials"
  }'
Field Name Description
grant_type
required
Valid values:
- client_credentials

Response

Example Response

{
  "token_type": "bearer",
  "id_token": "ABCDE...",
  "expires_in": 3600
}
Field Name Description
id_token The id token for the client
expires_in The remaining live time of the access token in seconds.
token_type Valid values:
- bearer

Authenticating on behalf of a customer

POST https://api.moia.io/auth/token

In order to access the API in behalf of a customer, a client has to authenticate with an id token linking itself to the customer. To obtain such an id token, one can directly use the username and the password of a customer to get this token. You get these credentials when creating a new customer.

Note that the client must authenticate itself using HTTP basic access authentication with its client id as username and its client secret as password.

Request

Example Request

curl https://api.moia.io/auth/token \
  -X POST \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -u 'client-id:secret' \
  -d '
  {
    "grant_type": "password",
    "username": "f9c2a5cf-da9b-.....",
    "password": "test1234"
  }'
Field Name Description
username
required
The MOIA account’s customer id.
password
required
The MOIA account’s password.
grant_type
required
Valid values:
- password

Response

Example Response

{
  "token_type": "bearer",
  "id_token": "ABCDE...",
  "expires_in": 3600
}
Field Name Description
id_token The id token for the current customer
expires_in The remaining live time of the access token in seconds.
token_type Valid values:
- bearer

/customer

Supported Versions

application/json
application/vnd.moia+json
application/vnd.moia.v1+json

The customer endpoint provides an API to programmatically create new accounts for customers as well as update customer details. All requests to the customer API require client authentication.

Create a new customer

POST https://api.moia.io/customer

This endpoint provides functionality to create a new customer.

Request

Example Request

curl https://api.moia.io/customer \
  -X POST \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -H "moia-auth: CLIENT_ID_TOKEN" \
  -d '
  {
    "email": "test@moia.io",
    "phoneNumber": "+49 172 12345678",
    "firstName": "Max",
    "lastName": "Mustermann",
    "locale": "de"
  }'
Field Name Description
email
required
The MOIA customer e-mail address (has to be unique).
phoneNumber
required
The MOIA customer phone number.
firstName
required
The MOIA customer first name.
lastName
required
The MOIA customer last name.
locale
required
The MOIA customer locale.

Response

Example Response

{
  "id": "fdd3729c-118b-46c4-8dc1....",
  "password": "ce5asaoYuu....."
}
Field Name Description
id The id/username of the created customer
password The password of the created customer

Update an existing customer

PUT https://api.moia.io/customer/{customerId}

This endpoint provides functionality to update an existing customer.

Request

Example Request

curl https://api.moia.io/customer/{customerId} \
  -X PUT \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -H "moia-auth: CLIENT_ID_TOKEN" \
  -d '
  {
    "email": "test@moia.io",
    "phoneNumber": "+49 172 12345678",
    "firstName": "Max",
    "lastName": "Mustermann",
    "locale": "de"
  }'
Field Name Description
email
required
The MOIA customer e-mail address.
phoneNumber
required
The MOIA customer phone number.
firstName
required
The MOIA customer first name.
lastName
required
The MOIA customer last name.
locale
required
The MOIA customer locale.

Response

Example Response

{
  "email": "test@moia.io",
  "phoneNumber": "+49 172 12345678",
  "firstName": "Max",
  "lastName": "Mustermann",
  "locale": "de"
}
Field Name Description
email The e-mail address of the updated customer.
phoneNumber The phone number of the updated customer.
firstName The first name of the updated customer.
lastName The last name of the updated customer.
locale The locale of the updated customer.
GET https://api.moia.io/customer/{customerId}/consentVersions

This endpoint provides information regarding a customer’s consent to certain conditions of use for the MOIA service. The endpoint also provides the latest available version of the terms and a link to the respective document.

(Currently this reflects Terms & Conditions)

Request

Example Request

curl https://api.moia.io/customer/{customerId}/consentVersions \
  -X GET \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -H "moia-auth: CLIENT_ID_TOKEN"

The request does not need a body.

Response

Example Response

{
  "termsAndConditionsVersion": "1",
  "termsAndConditionsConfirmationDate": "2018-12-03T10:29:21+0000",
  "latestTermsAndConditionsVersion": "1",
  "latestTermsAndConditionsCreationDate": "2018-07-02T10:48:33+0000",
  "latestTermsAndConditionsContent": {
    "de": "https://versions.customer.int.moia-group.io/de_tc_v_1.html",
    "en": "https://versions.customer.int.moia-group.io/en_tc_v_1.html"
  }
}
Field Name Description
termsAndConditionsVersion The Terms & Conditions version that a customer has confirmed.
Empty in the case that customer has not yet confirmed any version.
termsAndConditionsConfirmationDate Date that the customer has confirmed this version of Terms & Conditions
latestTermsAndConditionsVersion Latest available version of the MOIA Terms & Conditions
latestTermsAndConditionsCreationDate Date of issue of the latest MOIA Terms & Conditions
latestTermsAndConditionsContent Map containing links to localized texts of latest Terms & Conditions with locale as key

Confirm “Terms & Conditions”

PUT https://api.moia.io/customer/{customerId}/confirmTermsAndConditions

Customers have to confirm to MOIAs Terms & Conditions to access the provided service. Use this endpoint to submit the version that a customer has confirmed.

Request

Example Request

curl https://api.moia.io/customer/{customerId}/confirmTermsAndConditions \
  -X PUT \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -H "moia-auth: CLIENT_ID_TOKEN" \
  -d '
  {
    "version": "1"
  }'
Field Name Description
version The version of Terms & Conditions the customer confirmed.

Response

Example Response

{
  "version": "1",
  "confirmationDate": "2018-12-03T10:29:21+0000"
}
Field Name Description
version The version of Terms & Conditions the customer confirmed.
confirmationDate Date of customer’s confirmation to this version.

/serviceareas

Supported Versions

application/json
application/vnd.moia+json
application/vnd.moia.v1+json
application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v1+x-protobuf

The serviceareas endpoint provides the list of service areas you have access to. Carefully notice that each service area has a unique id and a specific validFrom from which the service area is valid. Moreover, a service area can have multiple versions - identified by their version.versionId - but only 1 will ever be published/activated/released at a time. By storing the versionId, one can quickly find out if this service area differs, without looking at the individual attributes.

Requests to this endpoint require client authentication.

Example Request

curl https://api.moia.io/serviceareas \
  -X GET \
  -H "accept: application/json" \
  -H "moia-auth: CLIENT_ID_TOKEN"

Response

Example Response

{
  "serviceAreas": [
    {
      "id": "de-hamburg-01",
      "legacyUuid": "f89ee35c-10ff-4d3b-959e-07d2e7637119",
      "version": {
        "versionId": "1561375291637",
        "versionLabel": "draft-1",
        "publishedDate": "2019-06-24T11:21:31.835807Z"
      },
      "locationAttributes": {
        "area": {
          "locations": [
            {
              "lat": 52.340704,
              "lon": 9.756997
            },
            {
              "lat": 52.341027,
              "lon": 9.761054
            },
            {
              "lat": 52.343193,
              "lon": 9.775219
            }
          ]
        },
        "topLeft": {
          "lat": 53.69651479,
          "lon": 9.71578068
        },
        "bottomRight": {
          "lat": 52.34,
          "lon": 10.2685932
        },
        "timeZone": "Europe/Berlin",
        "city": "Hamburg",
        "country": "DE"
      },
      "created": "2019-06-24T11:21:31.637311Z",
      "modified": "2019-06-24T11:21:31.835807Z",
      "validFrom": "2019-06-24T11:21:41.352591Z",
      "defaultLanguage": "de",
      "displayNames": {
        "de": "Hamburg",
        "en": "Hamburg"
      },
      "serviceParameters": {
        "maxSeats": 6,
        "childSeats": 1,
        "wheelchairSeats": 1
      }
    }
  ]
}
Field Name Description
id Unique identifier of a service area, in human-readable format.
legacyUuid Legacy Unique identifier of a service area, in UUID format.
version Service areas can have multiple versions, where only one is exclusively activate.
locationAttributes Location of the service area.
created Service area creation date.
modified Service area last modified date.
validFrom Date when the service area is valid from.
defaultLanguage Service Area default language.
displayNames List of Service Area names in different locales. Currently only de and en are supported.
serviceParameters Different parameters for the service area.

Version

Field Name Description
versionId Id of the service area version, A number that increases in a non-iterative order.
versionLabel Label or name of the service area version, enter by a person.
publishedDate When this version was published/activated/released. Date format is UTC.

locationAttributes

Field Name Description
area Polygon with an array of points (lat and lon) delineating the exact boundaries of the service area.
topLeft
bottomRight
Latitude and Longitude of rectangle that surrounds the service area.
The boundaries of the service area are all contained inside this rectangle.
timeZone Timezone of location where the service area is, in accordance with the tz database.
city City where the service area is, written in English.
country Country where the service area is, written in English.

serviceParameters

Field Name Description
maxSeats Max number of seats that can be booked in a vehicle operating inside this service area.
childSeats Max number of child seats that can be booked in a vehicle operating inside this service area.
wheelchairSeats Max number of wheel chair seats that can be booked in a vehicle operating inside this service area.

/servicehours

Supported Versions

application/json
application/vnd.moia+json
application/vnd.moia.v1+json

Request

curl https://api.moia.io/servicehours/hamburg \
  -X GET \
  -H "accept: application/vnd.moia.v1+json" \
  -H "content-type: application/vnd.moia.v1+json" \
  -H "moia-auth: CLIENT_ID_TOKEN"

The servicehours endpoint provides the service hours for the specified service area.

Requests to this endpoint requires client authentication.

/trip

A trip represents the customer journey from booking a trip, getting picked up and delivered by a vehicle and giving feedback for a trip.

The trip resource bundles HTTP calls with typical request / response cycle. For asynchronous events that are pushed to the partner backend, use the /events resource.

All endpoints require customer authentication.

/offer

POST https://api.moia.io/trip/offer

Supported Versions

application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v7+x-protobuf
application/json
application/vnd.moia+json
application/vnd.moia.v7+x-json

Make a trip offer based on origin and destination. The returned message contains one or more offers consisting of stop areas and time-windows for pickup and delivery as well as a price. One of them can be selected and used to make an order.

Request

Example Request

curl https://api.moia.io/trip/offer \
  -X POST \
  -H "accept: application/vnd.moia.v7+x-json" \
  -H "content-type: application/vnd.moia.v7+x-json" \
  -H "moia-auth: CUSTOMER_ID_TOKEN" \
  -d '
  {
    "origin": {
      "location": {
        "lat": 53.554415,
        "lon": 10.007321
      },
      "primaryAddress": "Heidi-Kabel-Platz 12",
      "secondaryAddress": "20099 Hamburg, Deutschland",
      "primaryPoiName": "Hamburg Hauptbahnhof",
      "secondaryPoiName": "Hamburg, Deutschland"
    },
    "destination":{
      "location":{
        "lat": 53.551324,
        "lon": 9.986031
      },
      "primaryAddress": "Stadthausbrücke 8",
      "secondaryAddress": "20355 Hamburg, Deutschland",
      "primaryPoiName": "MOIA Hamburg"
    },
    "seatInfo": {
        "adults": 1,
        "children": 0,
        "seats": 1,
        "childSeats": 0,
        "boosterSeats": 0,
        "wheelchairs": 0
    }
  }'
Field Name Description
origin
required
The starting point, see Named Location.
destination
required
The destination, see Named Location.
seatInfo
required
The seat info, see Seat Info

Named Location (origin & destination)

The origin and destination message contains the following attributes:

Field Name Description
location
required
The latitude and longitude of the starting point. Selection of the nearest stop is based on this.
primaryAddress
required
Street name. This is not used by the backend but will be returned in the response-object.
secondaryAddress
optional
City name. This is not used by the backend but will be returned in the response-object.
primaryPoiName
optional
Place of interest. This is not used by the backend but will be returned in the response-object.
secondaryPoiName
optional
Place of interest. This is not used by the backend but will be returned in the response-object.

Seat Info

The seatInfo message contains the following attributes:

Field Name Description
adults
required
The number of adults (aged 15 or older).
children
required
The number of children (younger than 15).
seats
required
The total number of seats required, includes childSeats, boosterSeats, and wheelchairs.
childSeats
optional
The number of child seats (child 9-36kg).
boosterSeats
optional
The number of booster seats (child >125cm / >22kg).
wheelchairs
optional
The number of wheelchairs.

For example, a request for one adult with a wheelchair would have the following seatInfo:

{
  "seatInfo": {
    "adults": 1,
    "children": 0,
    "seats": 1,
    "childSeats": 0,
    "boosterSeats": 0,
    "wheelchairs": 1
  }
}

Response

Example Response

{
  "tripRequest": {
    "tripId": "db6260fa-23e7-49c5-9dfb-df8cc25e2435",
    "serviceAreaUuid": "f89ee35c-10ff-4d3b-959e-07d2e7637119",
    "origin": {
      "location": {
        "lat": 53.554415,
        "lon": 10.007321
      },
      "primaryAddress": "Heidi-Kabel-Platz 12",
      "secondaryAddress": "20099 Hamburg, Deutschland"
    },
    "destination": {
      "location": {
        "lat": 53.551324,
        "lon": 9.986031
      },
      "primaryAddress": "Stadthausbrücke 8",
      "secondaryAddress": "20355 Hamburg, Deutschland"
    },
    "seatInfo": {
      "adults": 1,
      "children": 1,
      "seats": 2,
      "childSeats": 0,
      "boosterSeats": 0,
      "wheelchairs": 0
    },
    "requestTime": "2024-02-09T08:33:57.66Z"
  },
  "offers": [
    {
      "id": "09040572-34be-4cca-9fa4-38606be8a55b",
      "pickupArea": {
        "centerPoint": {
          "lat": 53.554415,
          "lon": 10.007321
        },
        "radius": 209,
        "walkingDuration": {
          "durationRange": {
            "minutesMin": 1,
            "minutesMax": 4
          }
        }
      },
      "deliveryArea": {
        "centerPoint": {
          "lat": 53.551324,
          "lon": 9.986031
        },
        "radius": 345,
        "walkingDuration": {
          "singleDuration": {
            "minutes": 3
          }
        }
      },
      "pickupTimeRange": {
        "start": "2024-02-09T08:43:57.775Z",
        "end": "2024-02-09T08:53:57.775Z"
      },
      "deliveryTimeRange": {
        "start": "2024-02-09T08:57:42.775Z",
        "end": "2024-02-09T09:07:27.788Z"
      },
      "signedPrice": {
        "price":  {
          "amount": "10.30",
          "currency": "EUR"
        },
        "signature": "763383308658400b6c5df5c2dc1454abc1de7d1951e30b798b268a95e09240e7",
        "priceParts": [
          {
            "type": "PRICE_TYPE_BASE_FEE",
            "price": {
              "amount": "4.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Base price for 1 adult",
                "text": ""
              },
              "de": {
                "header": "Basispreis für 1 Erwachsenen",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_SERVICE_FEE",
            "price": {
              "amount": "6.30",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Flexible price",
                "text": ""
              },
              "de": {
                "header": "Flexibler Preis",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_CHILDREN_DISCOUNT",
            "price": {
              "amount": "0.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Child",
                "text": "Rides free with you"
              },
              "de": {
                "header": "Kind",
                "text": "Kostenlose Mitfahrt"
              }
            }
          },
          {
            "type": "PRICE_TYPE_FUNDING_AREA_DISCOUNT",
            "price": {
              "amount": "-1.00",
              "currency": "EUR"
            },
            "isDiscount": true,
            "description": {
              "en": {
                "header": "Hamburg-Takt",
                "text": "Trips from this area are funded by the Federal Ministry of Digital Affairs and Transport"
              },
              "de": {
                "header": "Hamburg-Takt",
                "text": "Fahrten aus dem gewählten Stadtteil werden von dem Ministerium für Digitales und Verkehr gefördert"
              }
            }
          }
        ],
        "discountedPrice": {
          "amount": "9.30",
          "currency": "EUR"
        }
      },
      "serviceClass": "SERVICE_CLASS_CLASSIC",
      "metadata": "CgkIwRAQgKnOkwISVgoDCJ0IEAEaCQjgCBCAqc6TAiIAKkBPZmZlckNyZWF0b3JfRmxlZXRPcHRpbWl6YXRpb25TbWFydE9mZmVyc19IYXJkQ29kZWRGYWxsYmFja1ZhbHVlGgA="
    }
  ],
  "wheelchairOffers": [],
  "allOffers": [
    {
      "id": "09040572-34be-4cca-9fa4-38606be8a55b",
      "pickupArea": {
        "centerPoint": {
          "lat": 53.554415,
          "lon": 10.007321
        },
        "radius": 209,
        "walkingDuration": {
          "durationRange": {
            "minutesMin": 1,
            "minutesMax": 4
          }
        }
      },
      "deliveryArea": {
        "centerPoint": {
          "lat": 53.551324,
          "lon": 9.986031
        },
        "radius": 345,
        "walkingDuration": {
          "singleDuration": {
            "minutes": 3
          }
        }
      },
      "pickupTimeRange": {
        "start": "2024-02-09T08:43:57.775Z",
        "end": "2024-02-09T08:53:57.775Z"
      },
      "deliveryTimeRange": {
        "start": "2024-02-09T08:57:42.775Z",
        "end": "2024-02-09T09:07:27.788Z"
      },
      "signedPrice": {
        "price":  {
          "amount": "10.30",
          "currency": "EUR"
        },
        "signature": "763383308658400b6c5df5c2dc1454abc1de7d1951e30b798b268a95e09240e7",
        "priceParts": [
          {
            "type": "PRICE_TYPE_BASE_FEE",
            "price": {
              "amount": "4.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Base price for 1 adult",
                "text": ""
              },
              "de": {
                "header": "Basispreis für 1 Erwachsenen",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_SERVICE_FEE",
            "price": {
              "amount": "6.30",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Flexible price",
                "text": ""
              },
              "de": {
                "header": "Flexibler Preis",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_CHILDREN_DISCOUNT",
            "price": {
              "amount": "0.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Child",
                "text": "Rides free with you"
              },
              "de": {
                "header": "Kind",
                "text": "Kostenlose Mitfahrt"
              }
            }
          },
          {
            "type": "PRICE_TYPE_FUNDING_AREA_DISCOUNT",
            "price": {
              "amount": "-1.00",
              "currency": "EUR"
            },
            "isDiscount": true,
            "description": {
              "en": {
                "header": "Hamburg-Takt",
                "text": "Trips from this area are funded by the Federal Ministry of Digital Affairs and Transport"
              },
              "de": {
                "header": "Hamburg-Takt",
                "text": "Fahrten aus dem gewählten Stadtteil werden von dem Ministerium für Digitales und Verkehr gefördert"
              }
            }
          }
        ],
        "discountedPrice": {
          "amount": "9.30",
          "currency": "EUR"
        }
      },
      "serviceClass": "SERVICE_CLASS_CLASSIC",
      "metadata": "CgkIwRAQgKnOkwISVgoDCJ0IEAEaCQjgCBCAqc6TAiIAKkBPZmZlckNyZWF0b3JfRmxlZXRPcHRpbWl6YXRpb25TbWFydE9mZmVyc19IYXJkQ29kZWRGYWxsYmFja1ZhbHVlGgA="
    },
    {
      "id": "fba0b65e-aa25-4f71-89c9-2f69b653f3ac",
      "pickupTimeRange": {
        "start": "2024-02-09T08:43:57.775Z",
        "end": "2024-02-09T08:49:57.775Z"
      },
      "deliveryTimeRange": {
        "start": "2024-02-09T08:54:42.775Z",
        "end": "2024-02-09T09:01:17.594Z"
      },
      "signedPrice": {
        "price":  {
          "amount": "10.80",
          "currency": "EUR"
        },
        "signature": "8de7d2da57995ae9bdda4052cf79213532c4007d22659a459c702933dc92e858",
        "priceParts": [
          {
            "type": "PRICE_TYPE_BASE_FEE",
            "price": {
              "amount": "4.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Base price for 1 adult",
                "text": ""
              },
              "de": {
                "header": "Basispreis für 1 Erwachsenen",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_SERVICE_FEE",
            "price": {
              "amount": "6.30",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Flexible price",
                "text": ""
              },
              "de": {
                "header": "Flexibler Preis",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_EXPRESS_TRIP_FEE",
            "price": {
              "amount": "0.50",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Express fee",
                "text": ""
              },
              "de": {
                "header": "Express-Zuschlag",
                "text": ""
              }
            }
          },
          {
            "type": "PRICE_TYPE_CHILDREN_DISCOUNT",
            "price": {
              "amount": "0.00",
              "currency": "EUR"
            },
            "isDiscount": false,
            "description": {
              "en": {
                "header": "Child",
                "text": "Rides free with you"
              },
              "de": {
                "header": "Kind",
                "text": "Kostenlose Mitfahrt"
              }
            }
          },
          {
            "type": "PRICE_TYPE_FUNDING_AREA_DISCOUNT",
            "price": {
              "amount": "-1.00",
              "currency": "EUR"
            },
            "isDiscount": true,
            "description": {
              "en": {
                "header": "Hamburg-Takt",
                "text": "Trips from this area are funded by the Federal Ministry of Digital Affairs and Transport"
              },
              "de": {
                "header": "Hamburg-Takt",
                "text": "Fahrten aus dem gewählten Stadtteil werden von dem Ministerium für Digitales und Verkehr gefördert"
              }
            }
          }
        ],
        "discountedPrice": {
          "amount": "9.80",
          "currency": "EUR"
        }
      },
      "pickupStop": {
        "id": "191458",
        "name": {
          "de": "Hauptbahnhof / Hachmannplatz",
          "en": "Hauptbahnhof / Hachmannplatz"
        },
        "customerLocation": {
          "lat": 53.553638,
          "lon": 10.007842
        },
        "walkingDuration": "126s",
        "walkingDistance": 175
      },
      "deliveryStop": {
        "id": "216045",
        "name": {
          "de": "Stadthausbrücke 10",
          "en": "Stadthausbrücke 10"
        },
        "customerLocation": {
          "lat": 53.551441,
          "lon": 9.985918
        },
        "walkingDuration": "60s",
        "walkingDistance": 20
      },
      "serviceClass": "SERVICE_CLASS_EXPRESS",
      "metadata": "CgkI0wcQwOXDhgMSZAoDCI0EEAEaCQiCBBDA5cOGAyIAKkBPZmZlckNyZWF0b3JfRmxlZXRPcHRpbWl6YXRpb25TbWFydE9mZmVyc19IYXJkQ29kZWRGYWxsYmFja1ZhbHVlOgwI9caXrgYQwbPtvQI="
    }
  ]
}

Example Response for request with wheelchair in seatInfo

{
  "tripRequest": {
    "tripId": "db6260fa-23e7-49c5-9dfb-df8cc25e2435",
    "serviceAreaUuid": "f89ee35c-10ff-4d3b-959e-07d2e7637119",
    "origin": {
      "location": {
        "lat": 53.554415,
        "lon": 10.007321
      },
      "primaryAddress": "Heidi-Kabel-Platz 12",
      "secondaryAddress": "20099 Hamburg, Deutschland"
    },
    "destination": {
      "location": {
        "lat": 53.551324,
        "lon": 9.986031
      },
      "primaryAddress": "Stadthausbrücke 8",
      "secondaryAddress": "20355 Hamburg, Deutschland"
    },
    "seatInfo": {
      "adults": 1,
      "children": 0,
      "seats": 1,
      "childSeats": 0,
      "boosterSeats": 0,
      "wheelchairs": 1
    },
    "requestTime": "2020-05-28T17:50:25Z"
  },
  "offers": [],
  "wheelchairOffers": [
    {
      "id": "51751ffa-776e-41cf-953e-93d0009a1aa2",
      "pickupTimeRange": {
        "start": "2022-08-19T07:28:38.434643542Z",
        "end": "2022-08-19T07:38:38.434643542Z"
      },
      "deliveryTimeRange": {
        "start": "2022-08-19T07:52:35.434643542Z",
        "end": "2022-08-19T08:05:27.939824733Z"
      },
      "signedPrice": {
        "price": {
          "amount": "7.50",
          "currency": "EUR"
        },
        "signature": "89145d4d5be334dacd7dfe4fc88dca937f234b8b774e8fe4f20eb5f11ed33b16",
        ...
      },
      "pickupStop": {
        "id": "202305",
        "name": {
          "de": "Dorotheenstraße 29A",
          "en": "Dorotheenstraße 29A"
        },
        "customerLocation": {
          "lat": 53.580975,
          "lon": 10.009604
        },
        "walkingDuration": "60s",
        "walkingDistance": 28
      },
      "deliveryStop": {
        "id": "216045",
        "name": {
          "de": "Stadthausbrücke 10",
          "en": "Stadthausbrücke 10"
        },
        "customerLocation": {
          "lat": 53.551441,
          "lon": 9.985918
        },
        "walkingDuration": "60s",
        "walkingDistance": 20
      },
      "serviceClass": "SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE",
      "metadata": ...
    }
  ],
  "allOffers": [
    {
      "id": "51751ffa-776e-41cf-953e-93d0009a1aa2",
      "pickupTimeRange": {
        "start": "2022-08-19T07:28:38.434643542Z",
        "end": "2022-08-19T07:38:38.434643542Z"
      },
      "deliveryTimeRange": {
        "start": "2022-08-19T07:52:35.434643542Z",
        "end": "2022-08-19T08:05:27.939824733Z"
      },
      "signedPrice": {
        "price": {
          "amount": "7.50",
          "currency": "EUR"
        },
        "signature": "89145d4d5be334dacd7dfe4fc88dca937f234b8b774e8fe4f20eb5f11ed33b16",
        ...
      },
      "pickupStop": {
        "id": "202305",
        "name": {
          "de": "Dorotheenstraße 29A",
          "en": "Dorotheenstraße 29A"
        },
        "customerLocation": {
          "lat": 53.580975,
          "lon": 10.009604
        },
        "walkingDuration": "60s",
        "walkingDistance": 28
      },
      "deliveryStop": {
        "id": "216045",
        "name": {
          "de": "Stadthausbrücke 10",
          "en": "Stadthausbrücke 10"
        },
        "customerLocation": {
          "lat": 53.551441,
          "lon": 9.985918
        },
        "walkingDuration": "60s",
        "walkingDistance": 20
      },
      "serviceClass": "SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE",
      "metadata": ...
    }
  ]
}
Field Name Description
tripRequest Identifies and contains information about the trip request.
offers
deprecated
List of regular offers which contain different stop areas, time-windows and prices.
This list is empty if wheelchairs were selected in the seat info of the request.
Deprecated Use allOffers instead, filtering by service class SERVICE_CLASS_CLASSIC.
wheelchairOffers
deprecated
List of wheelchair offers which contain different stops, time-windows and prices.
This list is empty if wheelchairs were not selected in the seat info of the request.
Deprecated Use allOffers instead, filtering by service class SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE.
allOffers List of all proposed offers, regarless of service class.

Trip request

Field Name Description
tripId Unique identifier for the trip. Needed to order and cancel a trip.
serviceAreaUuid The unique identifier of the service area this trip is in (based on coordinates).
origin The starting point, see Named Location.
destination The destination, see Named Location.
seatInfo The seat info, see Seat Info.
requestTime A timestamp of when the offer was created.

Offer

The response contains a list of offers. During /trip/order, one of the offers from this allOffers list needs to be selected. An offer consists of the following fields:

Field Name Description
id The unique identifier of the offer.
pickupArea The area around the requested origin where later a pickup stop can be selected from, see here. This field is only set if this is an offer of service class SERVICE_CLASS_CLASSIC.
pickupStop The pickup stop, see here. This field is only set if this is an offer of service class SERVICE_CLASS_EXPRESS or SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE.
pickupTimeRange The time window in which the vehicle should arrive at the pickup stop.
Note that the vehicle is only guaranteed to wait until the beginning of this time range.
deliveryArea The area around the requested destination where later a delivery stop can be selected from, see here.
deliveryStop The delivery stop, see here. This field is only set if this is an offer of service class SERVICE_CLASS_EXPRESS or SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE.
deliveryTimeRange The time window in which the vehicle should arrive to the delivery stop.
signedPrice Contains the price information and a signature that may not be changed, see here for details.
serviceClass The service class of the offer. The possible values are SERVICE_CLASS_CLASSIC, SERVICE_CLASS_EXPRESS and SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE.
metadata Opaque offer metadata used by MOIA systems for the execution of the offer. Needs to be included in the order request.

An offer with service class SERVICE_CLASS_CLASSIC is the standard service offer.

An offer with service class SERVICE_CLASS_EXPRESS promises a faster delivery and contains defined stops that will be used for the trip, but this service class comes at an increased price for the customer, described by the price part PRICE_TYPE_EXPRESS_TRIP_FEE.

An offer with service class SERVICE_CLASS_WHEELCHAIR_ACCESSIBLE ensures a service that is accessible for customers using a wheelchair. If the requested seat info includes a positive number of wheelchairs, only offers of this service class will be returned. If no wheelchair spot is requested, an offer of this service class will not be returned.

If a trip offer could not be created due to a business reason, a 400 Bad Request is returned.

Signed Price

A signed price consists of the following fields:

Arguments Description
price The base (i.e. undiscounted) price of the offer.
signature A signature for the offer that may not be changed.
discountedPrice The discounted price of the offer, including all discounts listed in the price parts.
priceParts The list of parts that make up the price.

Price Parts

A price part consists of the following fields:

Name Description
type The type of the price part (see table below).
price The (partial) price amount that is covered by this part. Negative, if isDiscount is "true".
isDiscount A boolean flag indicating whether this price part is a discount "true".
description A description of the price part in different locales. Currently only "de" and "en" are supported.
The description body consists of a header title and a potentially empty text with further details.

Types of price parts:

Price Type Description
PRICE_TYPE_BASE_FEE The minimum payable tariff of the trip.
PRICE_TYPE_SERVICE_FEE The dynamic part of the price, depending on demand/supply, number of passengers, distance etc.
PRICE_TYPE_EXPRESS_TRIP_FEE The added surcharge in case the trip is express.
PRICE_TYPE_CHILDREN_DISCOUNT The discount for accompanied children (travelling with at least one adult).
PRICE_TYPE_UNACCOMPANIED_CHILDREN_DISCOUNT The discount for children travelling alone.
PRICE_TYPE_CAMPAIGN_DISCOUNT The discount coming from a MOIA campaign.
PRICE_TYPE_FUNDING_AREA_DISCOUNT A special discount in case the trip is to/from an area where MOIA gets funded by the goverment (see AWHT).
PRICE_TYPE_SEVERELY_DISABLED_DISCOUNT The discount for severely disabled people (they need a proof + they need to be registered at MOIA).
PRICE_TYPE_PUBLIC_TRANSPORT_SUBSCRIPTION_DISCOUNT The discount for HVV card holders.

Pickup and delivery areas (for Classic offers)

The pickupArea and deliveryArea objects contain the following fields:

Field Name Description
centerPoint Center-point of the circle. We expect that to be equal to the pin.
radius Radius of the circle in meters.
walkingDuration Estimated duration for the walk (to or from the closest and furthest stop).

The walkingDuration is either represented as a single duration or a duration range:

Field Name Description
singleDuration
optional
The Walking Duration in minutes for one exact value.
singleDuration.minutes Display as “X minutes”
durationRange
optional
A range of two different values. Display as “X–Y minutes”
durationRange.minutesMax Maximum walking duration
durationRange.minutesMin Minimum walking duration

Pickup and delivery stops (for Express and Wheelchair offers)

The pickupStop and deliveryStop objects contain the following fields:

Field Name Description
id The id of the stop.
name The name of the stop in German and English.
customerLocation The coordinates of the stop.
walkingDuration The duration in seconds to get to the stop.
walkingDistance The distance to the stop in meters.

Errors

See Errors for the general error strategy of the API. The following types of error codes can be returned from this route.

Error Code Description
CODE_GENERAL_ERROR An internal, technical error occurred.
CODE_NOT_WITHIN_SERVICE_HOURS The requested ride time is not within the service hours.
CODE_NOT_WITHIN_SEATS_RANGE Requested seats configuration can’t be served.
CODE_NOT_WITHIN_SERVICE_AREA The requested location is not inside a known service area.
CODE_NO_PICKUP_STOPS_NEAR No pickup stop found near the requested location.
CODE_NO_DELIVERY_STOPS_NEAR No delivery stop found near the requested location.
CODE_NO_PICKUP_AND_DELIVERY_STOPS_NEAR No pickup stop and no delivery stop found near requested locations.
CODE_WALK_IS_FASTER The combined walking distance to pickup and from delivery stop is longer than the distance of the trip.
CODE_KILL_SWITCH_ACTIVE The request was rejected due to an extremely high system load.

/order

POST https://api.moia.io/trip/order

Supported Versions

application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v7+x-protobuf
application/json
application/vnd.moia+json
application/vnd.moia.v7+x-json

Request

Example Request

curl https://api.moia.io/trip/order \
  -X POST \
  -H "accept: application/vnd.moia.v7+x-json" \
  -H "content-type: application/vnd.moia.v7+x-json" \
  -H "moia-auth: CUSTOMER_ID_TOKEN" \
  -d '{
  "tripRequest": {
    "tripId": "db6260fa-23e7-49c5-9dfb-df8cc25e2435",
    "serviceAreaUuid": "f89ee35c-10ff-4d3b-959e-07d2e7637119",
    "origin": {…},
    "destination": {…},
    "seatInfo": {…},
    "requestTime": "2020-05-28T17:50:25Z"
  },
  "offer": {
    "id": "09040572-34be-4cca-9fa4-38606be8a55b",
    "pickupArea": {…},
    "deliveryArea": {…},
    "pickupTimeRange": {…},
    "deliveryTimeRange": {…},
    "signedPrice": {…},
    "metadata": …
  }
}'

With the /trip/order endpoint an offer that the customer has selected is ordered.

Field Name Description
tripRequest
required
Identifies and contains information about the trip request, see Trip request
offer
required
The regular or wheelchair offer that the customer has selected, see Offer and Wheelchair Offer.

Response

Example Response

{}

A response with code 200 signals that the order was received and is being processed. A confirmation is sent asynchronously via the TRIP_COMMITTED event (or TRIP_REJECTED in case of failure). The pickup- and delivery-stop will be sent in PICKUP_STOP_FIXED and DELIVERY_STOP_FIXED events, the vehicle number in a VEHICLE_FIXED event.

Errors

See Errors for the general error strategy of the api. The following types of error codes can be returned from this route.

Error Code Description
CODE_GENERAL_ERROR An internal, technical error occurred.
CODE_TRIP_ACTIVE A trip is already active for the given customer.
CODE_TIMES_ARE_IN_THE_PAST One or more of the requested times are already in the past.
CODE_KILL_SWITCH_ACTIVE The request was rejected due to an extremely high system load.

/cancel

POST https://api.moia.io/trip/cancel

Supported Versions

application/x-protobuf
application/vnd.moia+x-protobuf
application/vnd.moia.v6+x-protobuf
application/json
application/vnd.moia+json
application/vnd.moia.v6+x-json

A booked offer can be cancelled by sending the tripId that was contained in the offer-response.

Request

Example Request

curl https://api.moia.io/trip/cancel \
  -X POST \
  -H "accept: application/vnd.moia.v6+x-json" \
  -H "content-type: application/vnd.moia.v6+x-json" \
  -H "moia-auth: CUSTOMER_ID_TOKEN" \
  -d '{
  "tripId": "db6260fa-23e7-49c5-9dfb-df8cc25e2435"
}'
Field Name Description
tripId
required
The id from the offer response.

Response

Example Response

{}

Response code 200 signals that everything is okay.

Errors

See Errors for the general error strategy of the api. The following types of error codes can be returned from this route.

Error Code Description
CODE_GENERAL_ERROR An internal, technical error occurred.
CODE_TRIP_TO_CANCEL_NOT_FOUND When the customer tries to cancel a trip that we don’t know.
CODE_CANCEL_REJECTED Cancellation of the trip was rejected because the trip is in a state where it cannot be cancelled not yet or no longer.

/events

The Events API provides the ability to subscribe to realtime events related to trips via webhooks. This means that you can receive realtime pickup, delivery and other events of ongoing trips that have been created by your partner account.

/webhook

Register a Webhook

curl https://api.moia.io/events/webhook \
  -X PUT \
  -H "accept: application/vnd.moia+json" \
  -H "content-type: application/vnd.moia+json" \
  -H "moia-auth: CLIENT_ID_TOKEN" \
  -d '{
  "url": "https://www.mydomain.com/webhook/moia",
  "secret": "SOME_SECRET",
  "format": "json"
}'

To register a web hook url, send the url and a validation key to https://api.moia.io/events/webhook. Events will be sent as JSON using HTTP POST to that url. Note that the url cannot contain port numbers.

The header x-moia-signature will contain the HMAC hex digest of the response body. The HMAC hex digest is generated using the sha256 hash function and the secret as the HMAC key.

Example of signature validation in JavaScript

For illustrative purposes, a JavaScript example is provided showing how to create an HMAC object and add data to it to produce a hex digest in order to verify a message signature using the pre-signed message from an HTTP request.

const compare = require("secure-compare"); // constant time compare to prevent timing attacks
const crypto = require("crypto");
const hmac = crypto.createHmac("sha256", "SOME_SECRET");

var requestBody = "..."; // body from HTTP request
var signatureHeader = "SIGNATURE"; // precomputed signature in the x-moia-signature header

hmac.update(requestBody);
var signature = hmac.digest("hex"); // re-computed signature for verification

if (compare(signature, signatureHeader)) {
  console.log("Signature matches");
} else {
  console.log("Signature does not match -> reject request");
}

Events

Example Delivery

curl https://www.mydomain.com/webhook/moia \
  -X POST \
  -H "content-type: application/json" \
  -H "x-moia-signature: SIGNATURE" \
  -d '{...}'

Payload

[
  {
    "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
    "timestamp": "2019-04-15T12:23:34Z",
    "eventType": "CUSTOMER_PICKED_UP",
    "eventTypeVersion": "1.0",
    "data": {
      "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
      "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b"
    }
  },
  ...
]

The format of the events is designed to incorporate data from different domains in a compatible and versioned way. They include a data field for the main payload, as well as several metadata fields:

Field Name Type Description
id String (UUID) A unique id that identifies this event.
timestamp String (timestamp) The date and time when this event occurred (not the time when it was sent), ISO 8601 formatted.
eventType String, see list below Identifies the format of the data field.
eventTypeVersion String ({major.minor}) Version for this particular eventType (each eventType has its own versioning).

Event Types

Event Type Current Version Description
TRIP_COMMITTED 1.0 The trip was accepted and a vehicle will be dispatched. We may still select a different vehicle before pickup.
TRIP_REJECTED 1.0 The trip was not committed and had to be rejected, no vehicle will be dispatched.
PICKUP_STOP_FIXED 1.0 The pickup stop for a trip was selected.
DELIVERY_STOP_FIXED 1.0 The delivery stop for a trip was selected.
VEHICLE_FIXED 1.0 The final vehicle for this trip was selected shortly before or at pickup. The assigned vehicle will not change anymore.
CUSTOMER_PICKED_UP 1.0 The customer was picked up by the vehicle.
CUSTOMER_DELIVERED 1.0 The customer was delivered by the vehicle.
CUSTOMER_NOT_SHOWN 1.0 The trip cannot be executed because the customer did not show up.
SERVICE_ABORTED 1.0 The trip could not be fulfilled anymore (due to technical errors)
VEHICLE_LOCATION_UPDATED 1.0 The current location of the vehicle was updated.
TRIP_SCHEDULE_UPDATED 1.0 The estimated time of pickup and arrival were updated.
TRIP_PAYMENT_CHARGEABLE 1.0 The customer should now be charged for the trip.
TRIP_PAYMENT_REFUND_REQUESTED 1.0 The customer has requested a refund, which was accepted by customer support.

TRIP_COMMITTED

TRIP_COMMITTED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "TRIP_COMMITTED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b",
    "schedule": {
      "pickup": "2018-12-04T15:51:20Z",
      "delivery": "2018-12-04T16:01:07Z"
    }
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
schedule Object Initial pickup and delivery schedule.

TRIP_REJECTED

TRIP_REJECTED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "TRIP_REJECTED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b"
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.

PICKUP_STOP_FIXED and DELIVERY_STOP_FIXED

PICKUP_STOP_FIXED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "PICKUP_STOP_FIXED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b",
    "stop": {
      "name": {
        "de": "Heidi-Kabel-Platz 2",
        "en": "Heidi-Kabel-Platz 2"
      },
      "displayLocation": {
        "lat": 53.554464874232266,
        "lon": 10.007511187204898
      },
      "walkingDistance": 13.0,
      "walkingDuration": "9.456s"
    }
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
stop Object Stop name, location, walking duration and walking distance.
stop.walkingDuration String Walking duration to the stop from the origin.
stop.walkingDistance Number Walking distance to the stop from the origin in meters.



DELIVERY_STOP_FIXED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "DELIVERY_STOP_FIXED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b",
    "stop": {
      "name": {
        "de": "Heidi-Kabel-Platz 2",
        "en": "Heidi-Kabel-Platz 2"
      },
      "displayLocation": {
        "lat": 53.554464874232266,
        "lon": 10.007511187204898
      },
      "walkingDistance": 13.0,
      "walkingDuration": "9.456s"
    }
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
stop Object Stop name, location, walking duration and walking distance.
stop.walkingDuration String Walking duration from the stop to the destination.
stop.walkingDistance Number Walking distance from the stop to the destination in meters.

VEHICLE_FIXED

VEHICLE_FIXED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "VEHICLE_FIXED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b",
    "vehicleInfo": {
      "id": "vehicle-hamburg-12",
      "label": "12"
    }
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
vehicleInfo Object Vehicle identifier and human readable label.

CUSTOMER_PICKED_UP and CUSTOMER_DELIVERED

CUSTOMER_PICKED_UP v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "CUSTOMER_PICKED_UP",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b"
  }
}

CUSTOMER_DELIVERED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "CUSTOMER_DELIVERED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b"
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.

CUSTOMER_NOT_SHOWN

CUSTOMER_NOT_SHOWN v1.0

{
  "id": "da133107-7611-457e-9c79-ab3ddf2180db",
  "timestamp": "2019-04-15T12:24:31Z",
  "eventType": "CUSTOMER_NOT_SHOWN",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "9f162a17-cecb-40bc-aee0-2663e1733933",
    "customerId": "14984dcb-44bb-4d1b-be9c-059adcc70886"
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.

SERVICE_ABORTED

Note: This event is only sent in extremely rare cases when the vehicle has technical difficulties and the trip cannot be fulfilled in any way anymore.

SERVICE_ABORTED v1.0

{
  "id": "5479e5eb-9c7c-46c4-a514-0b3ba2d716ee",
  "timestamp": "2019-04-15T12:23:34Z",
  "eventType": "SERVICE_ABORTED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "2b076baf-a253-4384-a743-fcc0662074eb",
    "customerId": "d81705a2-ce44-4d2a-930a-238c5faed50b"
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.

VEHICLE_LOCATION_UPDATED

VEHICLE_LOCATION_UPDATED v1.0

{
  "id": "67b0d34b-b8b6-4a82-a98c-6f74be5e2cc6",
  "timestamp": "2019-04-15T12:23:54Z",
  "eventType": "VEHICLE_LOCATION_UPDATED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "fa20e157-35cd-40f4-a3ef-93af2327ec75",
    "customerId": "7ae77bad-8cc2-4bb4-96fe-4feeefc6ea1a",
    "lat": 9.23763824,
    "lon": 52.2348738
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
lat Number Last known latitude of the vehicle.
lon Number Last known longitude of the vehicle.

TRIP_SCHEDULE_UPDATED

TRIP_SCHEDULE_UPDATED v1.0

{
  "id": "576fdd4a-b13b-45a1-9cde-2148412fa991",
  "timestamp": "2019-04-15T12:23:56Z",
  "eventType": "TRIP_SCHEDULE_UPDATED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "fa20e157-35cd-40f4-a3ef-93af2327ec75",
    "customerId": "7ae77bad-8cc2-4bb4-96fe-4feeefc6ea1a",
    "pickupTime": "2019-04-15T12:32:00Z",
    "deliveryTime": "2019-04-15T12:43:11Z"
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
pickupTime String (timestamp) Currently estimated time for the pickup. Format: ISO 8601
deliveryTime String (timestamp) Currently estimated time for the delivery. Format: ISO 8601

TRIP_PAYMENT_CHARGEABLE and TRIP_PAYMENT_REFUND_REQUESTED

Payment related events will be retried for up to 3 days in case they could not be delivered. The retry interval is at about every 10 minutes.

TRIP_PAYMENT_CHARGEABLE v1.0

{
  "id": "8b903d63-d347-497b-a00b-15e4576ed1e4",
  "timestamp": "2019-04-15T12:32:44Z",
  "eventType": "TRIP_PAYMENT_CHARGEABLE",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "fa20e157-35cd-40f4-a3ef-93af2327ec75",
    "customerId": "7ae77bad-8cc2-4bb4-96fe-4feeefc6ea1a",
    "paymentReference": "A7BXY",
    "charge": {
      "grossAmount": "8.34",
      "netAmount": "7.01",
      "vatRate": "0.19",
      "currency": "EUR"
    }
  }
}

TRIP_PAYMENT_REFUND_REQUESTED v1.0

{
  "id": "8b903d63-d347-497b-a00b-15e4576ed1e4",
  "timestamp": "2019-04-15T12:32:44Z",
  "eventType": "TRIP_PAYMENT_REFUND_REQUESTED",
  "eventTypeVersion": "1.0",
  "data": {
    "tripId": "fa20e157-35cd-40f4-a3ef-93af2327ec75",
    "customerId": "7ae77bad-8cc2-4bb4-96fe-4feeefc6ea1a",
    "paymentReference": "X5Q5NB",
    "charge": {
      "grossAmount": "8.34",
      "netAmount": "7.01",
      "vatRate": "0.19",
      "currency": "EUR"
    }
  }
}
Field Name Type Description
tripId String (UUID) Identifier for this trip.
customerId String (UUID) Identifier for the customer.
paymentReference String (max length 8) The book-keeping reference for this transaction.
charge Object The charge for this transaction.
charge.grossAmount String Charged gross amount.
With the number of decimals that is standard for the currency (e.g. 2 decimals for EUR).
charge.netAmount String Charged net amount.
With the number of decimals that is standard for the currency (e.g. 2 decimals for EUR).
charge.vatRate String Charged VAT rate. Number between 0 and 1 that has at least 2 decimals.
charge.currency String Currency as ISO 4217 code.