Skip to main content

Introduction

What is TOMP?

TOMP (Transport Operator MaaS Provider) is an open API standard that defines how transport operators (like Voi) communicate with MaaS providers (transit apps, journey planners). It standardizes the full trip lifecycle: planning, booking, execution, and support.

Voi implements a minimal subset of TOMP v1.6.1 focused on e-scooter and e-bike rentals.

API contract

Voi supports a subset of the TOMP v1.6.1 specification. We commit to maintaining backward compatibility with the endpoints documented here. New endpoints may be added over time.

All major updates will be communicated to active partners via email before implementation.

Development

Test your integration against our staging environment. In staging, we provide virtual scooters in Berlin.

  • Vehicle locations do not change during a rental.
  • Zones and vehicles cover a larger area than in production.

For a broader description of the integration process, check our deep integration checklist.

Rate Limit

Default rate limit: 4000 requests per minute (sum of all requests). HTTP 429 is returned when exceeded.

For higher rate limits, contact our engineering support.

Staging & Production

EnvironmentBase URL
Staginghttps://api.stage.voiapp.io/edge/tomp
Productionhttps://api.voiapp.io/edge/tomp

Authentication

All requests require an X-Access-Token header. Contact our engineering support to receive your API key.

Endpoints

Operator Information

Get Regions

Returns the geographic regions where Voi operates.

curl "https://api.voiapp.io/edge/tomp/operator/regions" \
-H "X-Access-Token: $TOKEN"

GET /operator/regions

Response 200

[
{
"regionId": "123",
"name": "Berlin",
"type": "OPERATING",
"serviceArea": {
"type": "Polygon",
"coordinates": [[[13.3, 52.5], [13.4, 52.5], [13.4, 52.6], [13.3, 52.6], [13.3, 52.5]]]
}
}
]
FieldTypeDescription
regionIdstringUnique identifier for the region (Voi zone ID)
namestringPublic name of the region
typestringArea type. This endpoint only returns OPERATING regions
serviceAreaGeoJSON PolygonGeographic boundary of the region

Get Operator Metadata

Returns information about the running implementation, including supported endpoints and scenarios.

curl "https://api.voiapp.io/edge/tomp/operator/meta" \
-H "X-Access-Token: $TOKEN"

GET /operator/meta

Response 200

[
{
"version": "1.6.1",
"baseUrl": "https://api.voiapp.io/edge/tomp",
"endpoints": [
{ "method": "POST", "path": "/bookings/one-stop", "status": "IMPLEMENTED" },
{ "method": "GET", "path": "/bookings/{id}", "status": "IMPLEMENTED" },
{ "method": "GET", "path": "/legs/{id}", "status": "IMPLEMENTED" },
{ "method": "POST", "path": "/legs/{id}/events", "status": "IMPLEMENTED" },
{ "method": "GET", "path": "/operator/meta", "status": "IMPLEMENTED" },
{ "method": "GET", "path": "/operator/pricing-plans", "status": "IMPLEMENTED" },
{ "method": "GET", "path": "/operator/regions", "status": "IMPLEMENTED" },
{ "method": "POST", "path": "/support", "status": "IMPLEMENTED" }
],
"scenarios": ["POSTPONED_COMMIT", "PAY_WHEN_FINISHED"],
"processIdentifiers": {
"operatorInformation": [],
"planning": [],
"booking": ["AUTO_COMMIT"],
"tripExecution": [],
"support": [],
"payment": [],
"general": []
}
}
]

Get Pricing Plans

Returns pricing information for Voi vehicles in each region.

curl "https://api.voiapp.io/edge/tomp/operator/pricing-plans" \
-H "X-Access-Token: $TOKEN"

GET /operator/pricing-plans

Response 200

[
{
"planId": "plan-123",
"regionId": "456",
"name": "Standard E-Scooter",
"isTaxable": true,
"description": "Pay per minute pricing",
"fare": {
"estimated": false,
"parts": [
{ "amount": 1.0, "currencyCode": "EUR", "type": "FIXED", "name": "Unlock fee" },
{ "amount": 0.19, "currencyCode": "EUR", "type": "FLEX", "unitType": "MINUTE", "units": 1, "name": "Per minute" }
]
}
}
]

Booking

Create One-Stop Booking

Creates a booking in a single request. Combines planning and booking — provide a location and optionally a specific vehicle, and receive a booking directly.

curl -X POST "https://api.voiapp.io/edge/tomp/bookings/one-stop" \
-H "X-Access-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"from": {
"coordinates": { "lng": 13.3888, "lat": 52.5170 }
},
"useAssets": ["vehicle-id-123"],
"customer": {
"externalId": "user-abc-456"
}
}'

POST /bookings/one-stop

Request body

FieldTypeRequiredDescription
fromPlaceyesDeparture location (lon/lat)
useAssetsstring[]yesSpecific vehicle IDs to book (exactly one)
customerCustomeryesEnd user information
customer.externalIdstringyesYour identifier for this user

Response 201

{
"id": "booking-789",
"state": "CONFIRMED",
"from": {
"coordinates": { "lng": 13.3888, "lat": 52.5170 }
},
"legs": [
{
"id": "leg-789",
"state": "NOT_STARTED",
"assetType": {
"id": "escooter",
"assetClass": "OTHER",
"assetSubClass": "scooter"
},
"asset": {
"id": "vehicle-id-123",
"stateOfCharge": 85,
"maxRange": 12000
},
"pricing": {
"estimated": true,
"parts": [
{ "amount": 1.0, "currencyCode": "EUR", "type": "FIXED" }
]
}
}
]
}

Booking states: STARTEDSTART_FINISHINGFINISHED

Get Booking

Retrieve the current state of a booking.

curl "https://api.voiapp.io/edge/tomp/bookings/booking-789" \
-H "X-Access-Token: $TOKEN"

GET /bookings/{id}

Returns the same booking object as the create response, with updated state.

Trip Execution

Get Leg

Retrieve the current state of a leg (trip segment).

curl "https://api.voiapp.io/edge/tomp/legs/leg-789" \
-H "X-Access-Token: $TOKEN"

GET /legs/{id}

Response 200

{
"id": "leg-789",
"state": "IN_USE",
"departureTime": "2026-04-15T10:30:00Z",
"assetType": {
"id": "escooter",
"assetClass": "OTHER",
"assetSubClass": "scooter"
},
"asset": {
"id": "vehicle-id-123",
"stateOfCharge": 72,
"maxRange": 9500
},
"pricing": {
"estimated": false,
"parts": [
{ "amount": 1.0, "currencyCode": "EUR", "type": "FIXED", "name": "Unlock fee" },
{ "amount": 2.85, "currencyCode": "EUR", "type": "FLEX", "unitType": "MINUTE", "units": 15, "name": "Ride time" }
]
}
}

Leg states: STARTEDSTART_FINISHINGFINISHED

Post Leg Event

Send an event to change the state of a leg.

curl -X POST "https://api.voiapp.io/edge/tomp/legs/leg-789/events" \
-H "X-Access-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event": "FINISH",
"userLocation": {
"coordinates": { "lng": 13.3920, "lat": 52.5200 }
}
}'

POST /legs/{id}/events

FieldTypeRequiredDescription
eventstringyesFINISH or START_FINISHING
userLocationPlacenoUser's current location
assetAssetnoAsset state at event time
urlstring[]noSupporting media (e.g. parking photos)

Response 200

Returns the updated leg object with new state and final pricing.

Note: A 503 response with a Retry-After header may be returned for temporary issues (e.g. Bluetooth lock jammed). Retry after the specified number of seconds.

Support

Create Support Ticket

Submit a support request on behalf of the end user.

curl -X POST "https://api.voiapp.io/edge/tomp/support" \
-H "X-Access-Token: $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"id": "booking-789",
"supportType": "NOT_AT_LOCATION",
"time": "2026-04-15T10:45:00Z",
"contactInformationEndUser": "+49123456789",
"comment": "The scooter is not at the indicated location"
}'

POST /support

FieldTypeRequiredDescription
idstringyesThe booking ID
supportTypestringyesType of issue (see below)
timedatetimeyesWhen the issue occurred
contactInformationEndUserstringyesUser's contact info
commentstringyesDescription of the issue

Support types:

TypeDescription
BROKEN_DOWNVehicle is broken
NOT_AT_LOCATIONVehicle not found at expected location
MISSING_AFTER_PAUSEVehicle gone after pausing trip
NOT_CLEANVehicle is not clean
NOT_AVAILABLEVehicle not available
UNABLE_TO_OPENCannot unlock vehicle
UNABLE_TO_CLOSECannot lock vehicle
API_TECHNICALTechnical API issue
API_FUNCTIONALFunctional API issue
ACCIDENTAccident occurred
OTHEROther issue

Response 200

{
"id": "booking-789",
"supportType": "NOT_AT_LOCATION",
"time": "2026-04-15T10:45:00Z",
"contactInformationEndUser": "+49123456789",
"comment": "The scooter is not at the indicated location",
"status": "PROCESSING"
}

Support statuses: PROCESSING

Error Handling

All errors follow the TOMP error handling specification.

{
"status": 400,
"detail": "Invalid coordinates provided"
}
HTTP CodeDescription
400Bad request — invalid input
401Unauthenticated — missing or invalid token
403Forbidden — valid token but insufficient permissions
404Not found — resource doesn't exist
406Not acceptable — booking cannot be made (e.g. no vehicles available)
409Conflict — state conflict (e.g. booking already finished)
428Precondition required — e.g. user blocked by operator
503Service unavailable — temporary issue, check Retry-After header

Typical Flow

A typical integration follows this flow:

1. GET /operator/regions → Discover available zones
2. GET /operator/pricing-plans → Get pricing per zone
3. POST /bookings/one-stop → Book a specific vehicle
4. GET /legs/{id} → Monitor trip state
5. POST /legs/{id}/events → End the trip (FINISH)
6. GET /bookings/{id} → Get final booking with pricing

OpenAPI Specification

The full OpenAPI 3.0 specification for Voi's TOMP implementation is available at:

For the complete TOMP v1.6.1 standard, see: