NAV nav

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.

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

Environment Base URL
Staging https://api.stage.voiapp.io/edge/tomp
Production https://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]]]
    }
  }
]
Field Type Description
regionId string Unique identifier for the region (Voi zone ID)
name string Public name of the region
type string Area type. This endpoint only returns OPERATING regions
serviceArea GeoJSON Polygon Geographic 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

Field Type Required Description
from Place yes Departure location (lon/lat)
useAssets string[] yes Specific vehicle IDs to book (exactly one)
customer Customer yes End user information
customer.externalId string yes Your 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

Field Type Required Description
event string yes FINISH or START_FINISHING
userLocation Place no User’s current location
asset Asset no Asset state at event time
url string[] no Supporting 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

Field Type Required Description
id string yes The booking ID
supportType string yes Type of issue (see below)
time datetime yes When the issue occurred
contactInformationEndUser string yes User’s contact info
comment string yes Description of the issue

Support types:

Type Description
BROKEN_DOWN Vehicle is broken
NOT_AT_LOCATION Vehicle not found at expected location
MISSING_AFTER_PAUSE Vehicle gone after pausing trip
NOT_CLEAN Vehicle is not clean
NOT_AVAILABLE Vehicle not available
UNABLE_TO_OPEN Cannot unlock vehicle
UNABLE_TO_CLOSE Cannot lock vehicle
API_TECHNICAL Technical API issue
API_FUNCTIONAL Functional API issue
ACCIDENT Accident occurred
OTHER Other 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, UPDATE_REQUESTED, RESOLVED, CANCELLED

Error Handling

All errors follow the TOMP error handling specification.

{
  "status": 400,
  "detail": "Invalid coordinates provided"
}
HTTP Code Description
400 Bad request — invalid input
401 Unauthenticated — missing or invalid token
403 Forbidden — valid token but insufficient permissions
404 Not found — resource doesn’t exist
406 Not acceptable — booking cannot be made (e.g. no vehicles available)
409 Conflict — state conflict (e.g. booking already finished)
428 Precondition required — e.g. user blocked by operator
503 Service 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: