# Adapt2Move - Complete Documentation > Adapt2Move is a B2B Mobility API aggregator that combines new-world mobility services (car sharing, public transport) with traditional ones (car rental) into a single unified API for enterprise customers. --- ## Getting Started # Adapt2Move Developer Docs Build mobility integrations with a unified API that aggregates multiple mobility providers into a single, consistent interface. ## What is Adapt2Move? Adapt2Move provides a unified Mobility API for businesses with corporate travel needs. Instead of integrating with each mobility provider individually, you make one API call and get consolidated results with real-time pricing and availability. **Supported modalities:** Public transport (local and long-distance), car rental, car sharing, bike and scooter sharing — with additional modalities planned. ## Getting Started in 3 Steps --- ## Developer Tools Import the API schema into your favorite tool to explore and test endpoints: - **[OpenAPI Schema (JSON)](/mobility-api/v2/schema.json)** — Import into Postman, Insomnia, or any OpenAPI-compatible client - **[Interactive API Reference](/mobility-api/v2/docs)** — Browse endpoints with Scalar, try requests directly in the browser - **[Changelog](/docs/changelog)** — Latest updates and new features --- **Need help?** Email [info@adapt2move.de](mailto:info@adapt2move.de) with your `requestId` from the API response and a description of your use case. # Quick Start Get started with the Adapt2Move Mobility API in 5 minutes. This guide walks you through making your first successful search request. ## Prerequisites - **API Credentials**: An API key for authentication — create one in the [Dashboard Portal](/workspace) under your tenant settings - **HTTP Client**: curl, Postman, or your preferred programming language **Replace `YOUR_API_TOKEN`** with your actual API token. ### What This Request Does - **Searches** for available vehicles in Berlin (coordinates: 52.5200, 13.4050) - **Search radius**: Within 5km of the specified coordinates - **Pickup**: December 15, 2026 at 10:00 AM UTC - **Dropoff**: December 20, 2026 at 10:00 AM UTC (5-day rental) - **Same location**: Pickup and dropoff at the same coordinates ### Request Parameters Explained | Parameter | Description | | --------------------- | ------------------------------------------------------------------------------------------------- | | `pickup.coordinates` | Where to pick up the vehicle (latitude/longitude) | | `pickup.radiusMeters` | Search radius in meters (max 50km) | | `dates.start` | When to pick up (ISO 8601 format in UTC) | | `dates.end` | When to return (ISO 8601 format in UTC) | | `dropoff` | Optional: Different return location (for one-way rentals) | | `estimatedKilometers` | Estimated trip distance in km (required for pricing) | | `driverAge` | Optional: Age of the driver in years — affects rental eligibility and pricing for younger drivers | > **Required for Pricing**: Provide `estimatedKilometers` to receive pricing in your results. See [Booking Workflow](/docs/api/booking-workflow#estimated-kilometers) for details. > **Use Station Search First**: For better results, use the station search endpoint first to find exact pickup locations, then search using the returned place ID: **GET** `/mobility-api/v2/cars/stations` See [Booking Workflow - Location Discovery](/docs/api/booking-workflow#location-discovery) for when to use station search. # Authentication All API requests require authentication using API keys scoped to a tenant within your organization. This page covers how to obtain credentials, authenticate requests, and manage your keys. ## Getting Your Credentials Create API keys in the [Dashboard Portal](/workspace) under your tenant settings. Each key is scoped to a specific tenant within your organization, which means it automatically determines the data, rate limits, and policies that apply to your requests. When creating a key you choose one of two types — **Secret** or **Publishable** — and select the target environment. You can create separate keys for each environment and type. > **API Key Format**: Secret keys follow the format `a2m_{env}_{id}` (e.g. `a2m_live_abc123...`). Publishable keys follow the format `a2m_pk_{env}_{id}` (e.g. `a2m_pk_live_abc123...`). In both cases `env` is either `test` or `live`. Publishable keys are 95 characters long. Your API key implicitly carries both the tenant and organization context, so you never need to specify these IDs in your requests. For a detailed explanation of the tenant and organization hierarchy, see [Core Concepts](/docs/api/core-concepts#organizations--tenants). ## API Key Types The Adapt2Move API supports two key types designed for different runtime contexts. | | Secret | Publishable | | ---------------------- | ----------------------------- | ---------------------------------------- | | **Prefix** | `a2m_test_` / `a2m_live_` | `a2m_pk_test_` / `a2m_pk_live_` | | **Usage** | Server-side only | Client-side (browsers, embedded widgets) | | **Permissions** | Full (read + write, bookings) | Read-only | | **Domain restriction** | None | Required (Origin header) | | **CORS headers** | Not included | Automatic | Use **secret keys** whenever your code runs on a server you control — backend services, cron jobs, server-side rendering. Use **publishable keys** when you need to call the API directly from a browser, for example to power a map with live mobility data, a search widget, or an availability display. ## Publishable Keys Publishable keys are purpose-built for client-side use. They grant read-only access to a limited set of endpoints, so even if a key is extracted from your frontend source code, it cannot be used to create bookings or modify any data. The allowed permissions are: - `cars:offer:read` - `micromobility:offer:read` - `public-transport:offer:read` - `providers:read` - `multimodal:explore:read` ### Domain Configuration Every publishable key must be configured with one or more allowed domains in the [Dashboard Portal](/workspace). The API validates the `Origin` header on each request and rejects any origin that does not match. You can specify exact domains such as `app.example.com` or wildcard patterns such as `*.example.com`. Localhost origins are automatically allowed in non-production environments so you can develop without additional configuration. ### Browser Usage The API returns automatic CORS headers for publishable key requests, so standard browser `fetch` calls work without a proxy. > **Security Note on Origin Validation**: The Origin header is enforced at the browser level through CORS. It cannot be spoofed from a standard browser, but it can be set freely from server-side code. This means domain restriction is a convenience control, not an absolute security boundary. The real protection is that publishable keys only permit read-only access — even if a key is used outside the allowed origins, no bookings or writes can be made. ## Making Authenticated Requests Every API request must include your API key in one of two headers: `Authorization: Bearer ` (standard Bearer token) or `x-api-key: ` (alternative header). Both methods are equivalent and identify your tenant and organization automatically. **POST** `/mobility-api/v2/cars` ### Common Mistakes The most frequent authentication error is omitting the `Bearer` prefix from the Authorization header. Writing `Authorization: YOUR_API_TOKEN` instead of `Authorization: Bearer YOUR_API_TOKEN` will result in a 401 error. Similarly, never pass your API key as a URL query parameter — tokens in URLs end up in server logs, browser history, and proxy logs, creating a significant security risk. Always transmit credentials exclusively through request headers. ## Authentication Flow ```mermaid sequenceDiagram participant Client as Your Application participant Gateway as API Gateway participant Auth as Auth Service Client->>Gateway: Request with API Key Gateway->>Auth: Validate Key alt Secret Key Auth->>Gateway: Tenant + Org Context else Publishable Key Auth->>Auth: Verify Origin header alt Origin allowed Auth->>Gateway: Tenant + Org Context (read-only) else Origin not allowed Gateway->>Client: 403 Forbidden end end alt Valid Key Gateway->>Client: 200 OK (Results) else Invalid Key Gateway->>Client: 401 Unauthorized end ``` When you send a request, the gateway validates your API key and resolves the associated tenant and organization. For secret keys, the request proceeds immediately with the correct context. For publishable keys, the gateway additionally validates the `Origin` header against the key's allowed domains and returns a 403 error if the origin is not permitted. If the key itself is invalid, you receive a 401 error. ## Key Management You create, rotate, and revoke API keys in the [Dashboard Portal](/workspace). If a key is compromised, revoke it immediately in the Dashboard Portal or contact [info@adapt2move.de](mailto:info@adapt2move.de) for urgent assistance. > **Security Essentials**: Never use **secret** API keys in client-side code — browsers, mobile apps, or frontend JavaScript. Always route secret-key calls through your own backend. For client-side scenarios, use a **publishable** key instead; it is designed for browser use and limited to read-only permissions. Regardless of key type, store secret keys in environment variables or a secrets manager, never in source code or version control. Rotate production keys periodically and use separate keys for test and production environments. ## Common Authentication Errors | HTTP Status | Error Code | Meaning | | ----------- | ------------------------- | ------------------------------------------- | | 401 | `UNAUTHORIZED` | Invalid API key | | 401 | `AUTHENTICATION_REQUIRED` | Missing Authorization header | | 403 | `FORBIDDEN` | Origin not allowed for this publishable key | See [Error Handling](/docs/api/error-handling) for complete error documentation, or explore all endpoints in the interactive [OpenAPI Reference](/mobility-api/v2/docs). # Environments The Adapt2Move Mobility API provides two separate environments — Test and Production — so you can build and verify your integration before going live. Both environments are accessed through the same base URL (`https://adapt2move.de/mobility-api/v2/`), and the environment is determined entirely by the API key you use. Secret test keys are prefixed with `a2m_test_` and production keys with `a2m_live_`. Publishable keys follow the same convention: `a2m_pk_test_` for test and `a2m_pk_live_` for production. There is no separate hostname or URL path to remember; swapping the key is all it takes to switch environments. ## Test Environment The test environment is designed for development and integration work. When you search for vehicles using a test key, the API connects to provider sandbox systems where available, so the search results you receive reflect real offers from test systems with realistic data structures. However, bookings made in test mode are not forwarded to the actual providers. Instead, they are simulated and confirmed immediately, which means you can exercise the full booking workflow — from search through prebook to final booking — without incurring any costs or creating real reservations. This makes the test environment ideal for automated testing, CI pipelines, and iterative development. > **No Real Charges in Test Mode**: Bookings created with a test API key are simulated. They do not result in real reservations with providers and will never incur charges. ## Production Environment The production environment connects to live provider systems. Every booking you create with a production key is real and binding. Standard policies, fees, and cancellation terms apply to all production bookings, so make sure your integration has been thoroughly tested before switching to production keys. You should treat production bookings with the same care as any real commercial transaction. ## Developing with Environments The recommended workflow is to develop and test your integration entirely against the test environment first. Once you are confident that your implementation handles searches, prebookings, bookings, and error cases correctly, switch to production by replacing your test API key with your production key. Because both environments share the same base URL and API contract, no other code changes are required. > **Switching is Simple**: To go from test to production, replace your `a2m_test_` key with your `a2m_live_` key (or `a2m_pk_test_` with `a2m_pk_live_` for publishable keys). The base URL, endpoints, request formats, and response structures remain identical regardless of key type. ## Rate Limits Rate limits may differ between environments. The test environment is generally more lenient to accommodate rapid iteration during development, while the production environment enforces stricter limits appropriate for live traffic. If you encounter rate limit errors during testing, they are unlikely to carry over to production at the same thresholds, but you should still design your integration to handle `429 Too Many Requests` responses gracefully in both environments. ## Data Isolation Test and production data are completely isolated from each other. A booking created with a test key cannot be retrieved, modified, or cancelled using a production key, and vice versa. This separation extends to all resources — offers, bookings, and any associated references exist only within the environment where they were created. If you need to verify a booking in a specific environment, make sure you are using the corresponding API key. > **No Cross-Environment Access**: There is no way to access test data from production or production data from test. Always ensure you are using the correct API key for the environment you intend to work with. --- ## Guides # Core Concepts Understanding these core concepts will help you build robust integrations with the Mobility API. ## Multi-Provider Aggregation The Mobility API aggregates offers from multiple mobility providers in real-time. When you search for mobility offers, the platform queries all configured providers simultaneously and waits for them to respond, consolidating the results into a single, uniformly structured response. A hard timeout of 20 seconds ensures the request completes even if individual providers are slow. This means you make one API call and receive offers from all providers — no need to integrate with each provider separately. ```mermaid flowchart LR Client[Your Application] API[Adapt2Move API] Providers[Multiple Providers] Client -->|1. Single Request| API API -->|2. Query All| Providers Providers -->|3. Return Results| API API -->|4. Unified Response| Client style API fill:#6366f1,stroke:#4f46e5,color:#fff style Client fill:#10b981,stroke:#059669,color:#fff style Providers fill:#f59e0b,stroke:#d97706,color:#fff ``` Because provider response times vary, search requests can take anywhere from 1 to 20 seconds. Plan your UI accordingly with loading indicators so users understand results are being gathered. ## Organizations & Tenants The platform uses a two-level hierarchy to manage access and data isolation. An **Organization** is your top-level account, typically representing your company. It contains members, settings, and provider configurations. Within each organization, you can create multiple **Tenants** — sub-units that represent customers, projects, or cost centers. API keys are always scoped to a specific tenant. When you authenticate with an API key, the platform resolves both the tenant and the parent organization, then applies all relevant policies automatically. You never need to specify a tenant or organization ID in your requests. > **Tenant Scoping**: You do not need to specify a tenant or organization ID in your requests. The API key automatically determines the tenant context for every request. This hierarchy provides granular access control: each tenant has its own API keys with independent rate limits, and booking data is fully isolated between tenants — even within the same organization. Costs are tracked per tenant, enabling customer- or project-level billing. Organizations can manage all their tenants from the [Dashboard Portal](/workspace/tenants). ## Offer Lifecycle Offers move through distinct stages in the booking process. Understanding these stages is essential for building a smooth user experience. ```mermaid stateDiagram-v2 [*] --> Search: POST /cars Search --> OfferDetails: POST /cars/offer(Optional) Search --> Prebook: POST /cars/prebook OfferDetails --> Prebook: POST /cars/prebook Prebook --> Booking: POST /cars/bookings Booking --> [*]: Confirmed note right of Search Basic vehicle info 15-30 min validity Token: v1 end note note right of OfferDetails Full specifications Terms & conditions Token: v2 (refreshed) end note note right of Prebook Price locked Reservation held 10-15 min validity Token: v3 end note note right of Booking Confirmed reservation Booking ID assigned Token consumed end note ``` The flow begins with a **Search** that returns offers along with encrypted tokens. Each offer has a validity window of 15-30 minutes. Optionally, you can fetch **Details** for a specific offer to retrieve full specifications and terms — this also refreshes the token. Next, **Prebook** locks the price and creates a temporary reservation valid for 10-15 minutes, returning a booking token. Finally, **Booking** consumes the token and confirms the reservation. Each stage returns an updated token that you must pass to the next stage. Always use the latest token from the most recent API response. ## Token-Based Security Offers include encrypted `offerToken` strings that are required for progressing through the booking flow. These tokens update with each API call, so you should always use the latest one. Tokens have an expiration time indicated by the `usableUntil` timestamp. They are encrypted and signed, making them safe to expose to end users in your frontend. In practice, simply pass the token from one API response into the next request. The API handles validation internally and auto-corrects where possible if you use a slightly outdated token. When a token expires, you will receive an `OFFER_EXPIRED` error — at that point, start a new search rather than retrying with the same token. ## Response Envelope Structure Every API response uses a consistent envelope format for predictable parsing and error handling. ### Success Response ```json { "success": true, "data": { // Endpoint-specific payload (offers, booking details, etc.) }, "meta": { "requestId": "req_abc123-1731499800000", "timestamp": "2026-11-13T10:30:00Z" } } ``` ### Error Response ```json { "success": false, "error": { "code": "OFFER_EXPIRED", "message": "This offer has expired. Please search again.", "details": { // Optional additional error context } }, "meta": { "requestId": "req_abc123-1731499800000", "timestamp": "2026-11-13T10:30:00Z" } } ``` ### Response Fields | Field | Type | When Present | Description | | ---------------- | ------- | ------------ | --------------------------------------------------- | | `success` | boolean | Always | `true` if successful, `false` if error | | `data` | object | On success | Response payload (structure varies by endpoint) | | `error` | object | On failure | Error details with code and message | | `error.code` | string | On failure | Machine-readable error code (e.g., `OFFER_EXPIRED`) | | `error.message` | string | On failure | Human-readable error description | | `error.details` | object | Optional | Additional context (e.g., field validation errors) | | `meta.requestId` | string | Always | Unique request identifier for debugging | | `meta.timestamp` | string | Always | When the response was generated (ISO 8601 UTC) | Always include `requestId` when contacting support — this helps us quickly identify and debug issues. ## Data Formats The API uses standardized formats for common data types to ensure consistency across all endpoints. ### Prices All monetary amounts use the **smallest currency unit** (cents for EUR/USD, pence for GBP): ```json { "amount": 14280, "currency": "EUR" } ``` To display the amount, divide by 100 for EUR/USD/GBP currencies. For example, `14280` = 142.80 EUR, `9950` = 99.50 EUR, `125000` = 1,250.00 EUR. ### Dates and Times All timestamps use **ISO 8601 format in UTC**: `YYYY-MM-DDTHH:mm:ssZ`. Always send UTC times — the API handles timezone conversions automatically based on location context. ```json { "pickup": { "dateTime": "2026-12-01T10:00:00Z" }, "dropoff": { "dateTime": "2026-12-05T18:30:00Z" } } ``` ### Geographic Coordinates Locations use **decimal degrees** (WGS84 standard) with latitude ranging from -90 to +90 and longitude from -180 to +180: ```json { "type": "coordinates", "latitude": 52.520008, "longitude": 13.404954 } ``` ### Language Codes For localized content, use **ISO 639-1 language codes**: `"en"` for English or `"de"` for German. Supported languages vary by provider. ## Resource Identifiers All resource IDs use **prefixes** to indicate their type: | Prefix | Resource Type | Example | Usage | | ---------- | ------------- | -------------------------- | -------------------------------- | | `offer_` | Offer | `offer_abc123xyz` | Identify mobility offers | | `bk_` | Booking | `bk_xyz789def` | Reference confirmed bookings | | `prebook_` | Prebooking | `prebook_abc123` | Reference temporary reservations | | `req_` | Request | `req_abc123-1731499800000` | Track API requests for debugging | Include `requestId` in all support requests for faster issue resolution. ## Operation Characteristics Different API operations have different timing and retry characteristics that you should account for in your integration. Set timeouts appropriate to each operation: 25 seconds for searches (due to multi-provider aggregation), 30 seconds for booking creation (provider confirmation can be slow), and 10-15 seconds for other operations like details and prebook. Offer tokens act as natural idempotency keys for booking creation — each token can only produce one successful booking, so retrying with the same token after a network error is safe. Some bookings may return a `PENDING` status when the provider confirms asynchronously; in those cases, poll the booking endpoint for the final status. When retrying after errors, use the same token for network and server errors, but obtain a fresh token if the offer has expired or become unavailable. # Booking Workflow The booking process consists of multiple phases that guide users from location discovery to confirmation. This workflow applies to offer-based modalities such as car rental and car sharing. > **Micromobility works differently**: Bikes and scooters follow a trip-based flow instead of offers and bookings: unlock, ride, end. See the [Micromobility Trips guide](/docs/api/micromobility-trips). ## Workflow Overview Here is the recommended booking flow: ```mermaid sequenceDiagram participant User participant Client participant API User->>Client: 1. Enter location Client->>API: GET location/station search API->>Client: Nearby locations with identifiers User->>Client: 2. Choose location & enter details Client->>API: POST search for offers API->>Client: Aggregated offers from providers User->>Client: 3. Choose offer Client->>API: POST offer details (optional) API->>Client: Full details + refreshed token User->>Client: 4. Confirm selection Client->>API: POST prebook/reserve API->>Client: Reservation confirmation User->>Client: 5. Enter traveler info Client->>API: POST create booking API->>Client: Booking confirmed ``` **Minimum flow (direct)**: Search → Book (2 API calls) **Minimum flow (with prebook)**: Search → Prebook → Book (3 API calls) **Recommended flow**: Location Search → Search → Prebook → Book (4 API calls) **Full flow**: Location Search → Search → Details → Prebook → Book (5 API calls) ## Vehicle Access & Operator Accounts Some car-sharing providers hold several operator accounts (membership cards) and can only run **one active booking per account at a time**. When you book a vehicle, the API automatically picks a free account — you don't need to manage this yourself. Two response fields surface how this worked: - **`bookedAccount`** (on the booking response) - The account (`id`, optional `name`) the vehicle was actually reserved and billed on. Present only for providers with multiple accounts; absent for providers with a single account or no account concept at all. - **`appAccessAvailable`** (on the prebook response) - Whether the vehicle can be opened via smartphone instead of a physical access card. When `true`, you may set `accessMethod: "APP"` on the booking request; when omitted or `false`, bookings default to `accessMethod: "PHYSICAL_CARD"`. > **Why this matters**: A physical access card can only be in one vehicle at a time, so a second overlapping booking on the same account would conflict with the first. The API rotates to a different account automatically. If every account is already busy for the requested time window, booking fails with `NO_AVAILABLE_CARD` (see [Error Handling](#error-handling)) instead of silently double-booking. App-based access does not require a physical card, so it is exempt from this constraint. > **Child seats are an exception**: A child seat upsell is booked together with the vehicle on the **same** account, even though it technically overlaps it — this is intentional and never triggers rotation to a different account. ## Handling Booking States Always handle all possible booking states: | Status | Meaning | Next Action | | ------------- | ------------------- | --------------------------------------------------------------- | | **CONFIRMED** | Booking successful | Show confirmation, send voucher | | **PENDING** | Provider processing | Poll for updates, show waiting state (or to watch for it later) | | **FAILED** | Booking failed | Show error, offer to search again | ## Error Handling ### Common Errors | Error Code | When It Occurs | Solution | | -------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------- | | `OFFER_EXPIRED` | Token has expired | Start new search | | `OFFER_EXPIRED` | Offer sold out | Show error, offer alternatives | | `INVALID_TOKEN` | Token tampered or invalid | Start new search | | `INVALID_REQUEST` | Missing/invalid booking details | Fix validation errors, retry | | `NO_AVAILABLE_CARD` | Every operator account is already booked for this time window | Show error, suggest a different time | | `APP_ACCESS_NOT_AVAILABLE` | `accessMethod: "APP"` requested but the provider doesn't support it | Omit `accessMethod` or check `appAccessAvailable` first | See [Error Handling](/docs/api/error-handling) for complete error code reference. ## Best Practices ### Handle Timeouts Search requests can take 1-20 seconds. Show loading indicators and allow users to cancel long-running requests. ### Validate Before Booking Validate all required fields client-side before calling the booking endpoint to avoid unnecessary API calls. ### Implement Retry Logic - **Retry**: Network errors, timeouts (with exponential backoff) - **Don't retry**: Business errors (OFFER_EXPIRED, INVALID_REQUEST) ### Save Booking Confirmation Store booking ID, confirmation number, and voucher URL for future reference and support requests. ### Provide Clear Feedback - Loading states during API calls - Success confirmation with booking details - Clear error messages with next steps ## Booking Flows The API supports two booking flows. Choose based on your use case: ### Recommended: Prebook Flow **Search → Prebook → Book** This is the recommended flow for user-facing applications: ### Direct Booking Flow **Search → Book** You can skip the prebook phase and book directly using the `offerToken` from search results or offer details: ```json { "offerToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "driver": { ... } } ``` > **Important**: When using the direct booking flow: - You must have already shown the price to the client (i.e., `estimatedKilometers` was provided during search) - **Offer details do NOT include `requiredInformation`** - you must collect: email (always required), plus recommended: phoneNumber, fullName, and optional: age, licenseNumber, address, flightNumber, nationality - Upsell pricing is only available through the prebook endpoint - Pricing and availability are not locked until the booking is confirmed This flow is useful for: - Server-to-server integrations where you already know all driver information - Quick booking scenarios where prebook latency is unacceptable - Systems that don't need to display upsell pricing to users # Micromobility Trips Micromobility works differently from car booking. There is no offer, prebook, or booking object. You work with **trips**: find a vehicle nearby, reserve or unlock it, ride, and end the trip. Pricing runs per minute, so the final price is known when the trip ends. All requests need an API key, see [Authentication](/docs/api/authentication). Vehicle search works with publishable keys (`micromobility:offer:read`); the trip endpoints require a secret key with `micromobility:trip:write`. ## Trip Lifecycle ```mermaid sequenceDiagram participant User participant Client participant API User->>Client: 1. Open map / scan QR code Client->>API: POST vehicle search API->>Client: Nearby vehicles with tokens User->>Client: 2. Pick a vehicle Client->>API: POST trips (RESERVE) API->>Client: Trip RESERVED User->>Client: 3. Unlock and ride Client->>API: POST trips (START) API->>Client: Trip ACTIVE User->>Client: 4. Park mid-ride / locate vehicle Client->>API: POST vehicle-action (LOCK / UNLOCK / HONK) API->>Client: 202 Accepted User->>Client: 5. End the trip Client->>API: POST trips end API->>Client: Trip COMPLETED + final price ``` **Minimum flow**: Search → Start → End (3 API calls) **With reservation**: Search → Reserve → Start → End (4 API calls) ## Trip States Poll `GET /mobility-api/v2/micromobility/trips/{tripId}` to track a trip. Handle every state: | Status | Meaning | Next action | | ----------- | --------------------------------------------- | --------------------------------------- | | `RESERVED` | Vehicle held, ride not started | Start the trip or cancel | | `ACTIVE` | Ride in progress, timer running | Vehicle actions, end trip | | `PAUSED` | Vehicle locked mid-ride, timer keeps running | Unlock to continue, or end trip | | `COMPLETED` | Trip ended | Show the receipt from the end-trip call | | `CANCELLED` | Reservation cancelled, no charge for the ride | Back to search | > **Keep the end-trip response**: The `price` breakdown is returned in the response of the end-trip call. Polling the trip status does not include the price, so store the end-trip response for the receipt. ## Pricing Micromobility pricing is trip-based: an optional one-time unlock fee plus a per-minute rate from the vehicle's `pricingPlan` (where the provider publishes one). Paused minutes are billed like active minutes, because the vehicle stays assigned to the rider. The exact total arrives in the end-trip response. ## Error Handling | Situation | What you get | What to do | | --------------------------------- | ---------------------------------------------------- | --------------------------------------------- | | Vehicle token expired or invalid | `404 NOT_FOUND` ("Vehicle token expired or invalid") | Run a fresh search, get a new token | | Scanned vehicle ≠ the one started | `409 VEHICLE_MISMATCH` | Ask the rider to scan the correct vehicle | | Trip not found or already ended | `PROVIDER_ERROR` with a clear message | Refresh trip status, return to search | | Vehicle rejected the command | `PROVIDER_ERROR` | Retry once, then surface the provider message | See [Error Handling](/docs/api/error-handling) for the complete error code reference, and the [OpenAPI Reference](/mobility-api/v2/docs) for full request and response schemas. # Providers The Mobility API aggregates offers from multiple mobility providers behind a unified interface. While request and response formats are consistent across all providers, the underlying modality types have fundamentally different characteristics — pricing models, vehicle access patterns, and booking flows. Understanding these differences helps you build integrations that handle each modality correctly. ## Modality Types The API organizes providers into four modality categories. Each category has its own set of endpoints and response structures. Car rental and car sharing follow the offer-based pattern (search, offer, prebook, book); micromobility uses a trip-based flow (search, reserve, ride, end). | Modality | Description | Booking Support | | -------------------- | ------------------------------------------ | -------------------------------------------------------------------- | | **Car Rental** | Traditional rental with daily/weekly rates | Full (search, book, cancel, modify) | | **Car Sharing** | Short-term, time + distance pricing | Full (search, book, cancel) | | **Micromobility** | Bikes, scooters, e-mopeds | Trips: search, reserve, ride, vehicle control on supported providers | | **Public Transport** | Trains, buses, trams, regional transit | Search only (booking planned) | > **Growing Provider Network**: We continuously onboard new providers. The API is designed so that new providers appear automatically in your search results without any integration changes on your side — as long as they are enabled for your tenant. ## Station-Based vs. Free-Floating Providers fall into two access patterns that affect how users pick up and return vehicles: **Station-based** providers have fixed locations. Vehicles must be picked up and returned at designated stations. When you search by coordinates, the API returns offers grouped by nearby stations. Car rental (airport/city stations) and most car sharing providers follow this model. **Free-floating** providers allow vehicles to be picked up and dropped off anywhere within a service area. When you search by coordinates, results show individual vehicles near that location. Most micromobility providers (e-scooters, bikes) operate this way. The API handles both patterns transparently — you always search by coordinates and receive a list of offers. The `station` field in the response indicates whether the offer is tied to a fixed location or a free-floating vehicle. ## Pricing Models The most important distinction between provider types is how pricing works. This directly impacts how you display offers and compare them across providers. ### Car Rental — Daily/Weekly Rates Car rental providers charge fixed daily or weekly rates. The price you see is the price you pay, regardless of distance driven. - **Fuel policy:** `FULL_TO_FULL` — the customer refuels before returning - **Mileage:** Typically unlimited, no per-kilometer charges - **Price breakdown:** Base rate + taxes + fees, itemized in the response - **Currency:** Prices in cents (smallest currency unit) with currency code ### Car Sharing — Time + Distance Car sharing providers charge based on rental duration **and** distance driven. Fuel is included in the price. - **Fuel policy:** `FUEL_INCLUDED` — fuel/charging costs are built into the rate - **Mileage:** Charged per kilometer, included in the total price calculation - **Price breakdown:** Time component + distance component, requires `estimatedKilometers` for accurate pricing - **Currency:** Same normalized format as car rental ### The `estimatedKilometers` Parameter Because car sharing prices depend on distance, the API needs to know your expected trip distance to calculate comparable prices. This is where `estimatedKilometers` comes in: ``` POST /mobility-api/v2/cars { "pickup": { ... }, "dates": { ... }, "estimatedKilometers": 150 } ``` **Without `estimatedKilometers`:** Car sharing offers may return `pricing: null` in search results because the per-kilometer component cannot be calculated. Car rental offers are unaffected (they don't charge per km). **With `estimatedKilometers`:** All providers return full price breakdowns in the same normalized format, making direct cross-provider comparison possible. > **Always provide estimatedKilometers**: If your use case involves comparing car sharing and car rental offers side by side, always include `estimatedKilometers` in your search request. Without it, you cannot meaningfully compare prices across provider types. The parameter is also required for the offer detail, prebook, and booking endpoints on car sharing providers to ensure pricing accuracy throughout the booking flow. ### Micromobility Pricing Micromobility providers (bikes, scooters) use per-minute pricing with optional unlock fees. Vehicles in the search response carry a `pricingPlan` where the provider publishes one, so you can show the rate before the ride starts. The final price is calculated when the trip ends: unlock fee plus billed minutes, returned in the end-trip response. On trip-capable providers, paused minutes are billed like active minutes because the vehicle stays assigned to the rider. See the [Micromobility Trips guide](/docs/api/micromobility-trips) for the full trip lifecycle. ### Public Transport Pricing Public transport providers return journey-based pricing (single tickets, day passes). Pricing structures vary by region and transport authority. Subscription-based pricing (e.g., Deutschlandticket) is available through select providers. ## Cross-Usage Networks Some car sharing providers operate cross-usage networks ("Quernutzungsnetzwerk"), where a registration with one regional provider grants access to vehicles from partner providers across the network. The most prominent example is the **Stadtmobil network**, which spans 25+ regional providers across Germany. A user registered with Stadtmobil Stuttgart can book vehicles from Stadtmobil Karlsruhe, Stadtmobil Berlin, or any other network member. **The API handles this transparently.** When you search by location, you receive offers from all available network providers in the area — you never need to know which regional entity operates a specific vehicle. The `provider` field in the response identifies the regional provider for display purposes, but the booking flow is identical regardless of which network member owns the vehicle. ## Provider Configuration Each tenant in your organization can have its own provider configuration. This is managed in the [Dashboard Portal](/workspace) under your tenant's provider settings. ### What Gets Configured - **Credentials** — Provider-specific access credentials (API keys, access cards, contract IDs). These determine which providers your tenant can access and book through. - **Settings** — Non-credential configuration like preferred regions, default brands, or feature toggles. - **Enabled/Disabled** — Toggle individual providers on or off per tenant. ### System vs. Custom Providers **System providers** (most micromobility and public transport) are available to all tenants automatically. They use shared infrastructure and don't require per-tenant credentials. **Custom providers** (car rental, car sharing) require your organization to have a contract or account with the provider. You configure the credentials in your tenant settings, and the API uses them to authenticate requests on your behalf. This two-tier model means your tenants get immediate access to a broad set of mobility data (vehicle locations, transit routes) while car rental and car sharing bookings require explicit provider relationships. ## General Provider Behavior ### Response Times Response times vary across providers, typically ranging from 1 to 15 seconds. The API queries all relevant providers in parallel with a hard timeout of 20 seconds. Always show a loading indicator while waiting for search results. If a provider is temporarily unavailable or times out, the API still returns results from the remaining providers. Check the response for provider-level warnings indicating incomplete results. ### Offer Validity Offer tokens are typically valid for 15 minutes, but the exact expiration varies by provider and modality. Car sharing offers tend to have shorter validity windows because they reflect real-time vehicle availability. > **Always Check usableUntil**: Every offer includes a `usableUntil` timestamp. Do not attempt to prebook or book an offer whose token has expired — the request will fail with an `OFFER_EXPIRED` error. ### Prebooking Behavior Prebooking behavior differs between provider types: - **Car rental:** Prebooking creates a local reservation reference. Availability is **not guaranteed** until the final booking call succeeds — another customer could reserve the same vehicle class in between. - **Car sharing:** Some providers support actual server-side reservations during prebook, while others treat it as advisory. Always check the prebook response status to understand what guarantee you have. ### Required Information Some bookings require additional information that isn't known at search time. The `requiredInformation` field in the prebook response tells you what's needed — for example, a flight number for airport car rental locations. Collect this from the user and include it in the booking request. ### Unlock Credentials Car sharing booking confirmations may include vehicle unlock credentials (e.g., a PIN code or app instructions). If the confirmation response contains unlock information, make sure to surface it to the end user. # Best Practices This page covers performance optimization and user experience patterns for building high-quality integrations with the Mobility API. ## Performance Optimization Search requests trigger real-time aggregation across multiple providers, which means they are the most expensive operations in terms of latency and server load. Cache search results locally for short periods (up to 5 minutes) to avoid redundant requests when users navigate back to results they have already seen. Implement request debouncing on user-driven inputs like location or date changes so that rapid modifications do not fire multiple simultaneous searches. Fetch offer details only when the user explicitly requests them — do not preload details for every offer in a search result set. Similarly, only call the prebook endpoint when the user is ready to proceed to checkout, since prebooking locks inventory and has a limited validity window. When using paginated endpoints, choose a page size that balances between too many requests (small pages) and unnecessarily large payloads (large pages); 20-50 items per page works well for most use cases. Set appropriate client-side timeouts for each operation type. Searches can take up to 20 seconds due to multi-provider aggregation, so a 25-second timeout is recommended. Booking creation may take up to 15 seconds and should have a 30-second timeout. For other operations like offer details and prebook, 10-15 seconds is sufficient. When retrying after server errors, use exponential backoff (1s, 2s, 4s, 8s) with a maximum of 3-5 attempts to avoid overwhelming the API. See [Error Handling](/docs/api/error-handling#retry-strategy) for the complete retry strategy. ## Rate Limits The API enforces rate limits to ensure fair usage and platform stability. Limits are applied per API key using a sliding window algorithm. ### Default Limits | Context | Limit | Window | | ------- | ----- | ------ | | Standard API key | 100 requests | 60 seconds | | Custom (per key) | Configurable | Configurable | Organization administrators can configure custom rate limits per API key in the Dashboard Portal. Contact your account manager for higher limits. ### Rate Limit Headers Every API response includes rate limit headers: | Header | Description | | ------ | ----------- | | `X-RateLimit-Limit` | Maximum requests allowed in the current window | | `X-RateLimit-Remaining` | Requests remaining in the current window | | `X-RateLimit-Reset` | Unix timestamp when the window resets | ### Handling 429 Responses When you exceed the rate limit, the API returns a `429 Too Many Requests` status. Implement exponential backoff when retrying: > **Best Practice**: Monitor the `X-RateLimit-Remaining` header proactively. Throttle your requests before hitting the limit rather than relying on retry logic after receiving a 429. ## User Experience Patterns ### Loading States and Search Feedback Because multi-provider searches can take anywhere from 1 to 20 seconds, always display a loading indicator or progress spinner during search operations. Consider showing a message that explains results are being gathered from multiple providers — this sets appropriate expectations and reduces perceived wait times. Allow users to cancel long-running searches if they want to modify their criteria. ### Offer Freshness Offers reflect real-time availability and pricing. Prices can change at any moment unless a prebooking has been created, and availability shifts as other users book similar offers. Do not cache search results for longer than 5 minutes, and consider showing a "results may have changed" indicator if the user returns to stale results. When an offer expires during the booking flow, guide the user back to search with a clear message rather than showing a generic error. ### Policy Display Display cancellation policies, terms, and conditions before the user commits to a booking. Highlight non-refundable bookings prominently so users are not surprised later. Show refund amounts clearly when applicable. For bookings with restrictive policies, require explicit user acceptance before proceeding to ensure informed consent. ### Booking Confirmation After a successful booking, display the booking status (`CONFIRMED`, `PENDING`, or `FAILED`) clearly in your UI. Show the confirmation number and voucher details prominently, along with clear next steps for the user. Note that the Adapt2Move platform sends confirmation and cancellation emails automatically — if you prefer to handle these yourself, contact our developer team to opt out. # Integration Demo Learn how to use the Mobility API by exploring interactive demos that show real user flows. Click through the demo on the left, and see the corresponding API calls update on the right. ## Car Search & Booking Flow This interactive demo walks you through the complete car rental booking flow. **Click through the steps in the demo** to see the matching API requests and responses. --- ## API Reference # Error Handling All API responses use a consistent envelope format. See [Core Concepts](/docs/api/core-concepts#response-envelope-structure) for the full structure. This page focuses on error categories, retry strategies, and how to handle common error scenarios. ## Error Categories ### 4xx Client Errors Errors caused by invalid requests. Fix the request before retrying. | HTTP Status | Error Code | Meaning | Solution | | ----------- | -------------------------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | | 400 | `INVALID_REQUEST` | Validation failed | Check `error.details` for field-specific errors | | 400 | `INVALID_LOCATION` | Invalid location provided | Check location format or use valid locationId | | 400 | `INVALID_DATE_RANGE` | Date range is invalid | Ensure pickup is before return, dates in future | | 400 | `INVALID_OFFER_TOKEN` | Token format invalid/corrupted | Get fresh offer token from search/prebook | | 400 | `MISSING_REQUIRED_FIELD` | Required field not provided | Check `error.details` for missing fields | | 400 | `APP_ACCESS_NOT_AVAILABLE` | `accessMethod: "APP"` requested but the provider doesn't support it | Omit `accessMethod` or check `appAccessAvailable` in the prebook response first | | 401 | `UNAUTHORIZED` | Invalid/expired token | Refresh API token | | 401 | `AUTHENTICATION_REQUIRED` | Missing Authorization header | Add Bearer token | | 403 | `FORBIDDEN` | Insufficient permissions | Contact support for access | | 404 | `NOT_FOUND` | Resource doesn't exist | Verify resource ID | | 404 | `NO_OFFERS_AVAILABLE` | No offers match criteria | Broaden search dates/locations | | 409 | `OFFER_EXPIRED` | Offer token expired | Start new search | | 409 | `OFFER_EXPIRED` | Offer sold out | Show alternative offers | | 409 | `OFFER_TOKEN_ALREADY_USED` | Token was already used | Get fresh offer token from search/prebook | | 409 | `MISSING_REQUIRED_FIELD` | Provider needs additional fields | Collect information, retry with same token | | 409 | `BOOKING_FAILED` | Booking could not be created | Check error details, may need new search | | 409 | `NO_AVAILABLE_CARD` | Every operator account the provider holds is already booked for this time window | Show error, suggest a different time | | 422 | `INVALID_REQUEST` | Invalid field values | Fix validation errors in `details` | | 429 | `RATE_LIMIT_EXCEEDED` | Too many requests | Wait and retry with backoff | ### 5xx Server Errors Temporary server issues. Safe to retry with exponential backoff. | HTTP Status | Error Code | Meaning | Solution | | ----------- | ------------------------- | ----------------------- | --------------------------------- | | 500 | `INTERNAL_ERROR` | Server error | Retry with backoff | | 500 | `PROVIDER_ERROR` | Provider returned error | Retry with backoff | | 501 | `OPERATION_NOT_SUPPORTED` | Feature not supported | Use alternative endpoint/provider | | 503 | `SERVICE_UNAVAILABLE` | Temporary outage | Retry after delay | | 504 | `TIMEOUT` | Operation timed out | Retry with backoff | ## Field Validation Errors When you receive an `INVALID_REQUEST`, the `error.details` object contains field-specific issues that you can map directly to your form fields: ```json { "success": false, "error": { "code": "INVALID_REQUEST", "message": "Request validation failed", "details": { "origin.dateTime": "Must be in the future", "destination.dateTime": "Must be after origin time" } } } ``` Parse the keys in `error.details` to identify which fields need correction and display inline validation messages to the user. Do not retry until the user has fixed the input. ## Retry Strategy ```mermaid flowchart TD A[API Request] --> B{Success?} B -->|Yes| C[Process Response] B -->|No| D{Error Type?} D -->|4xx Client Error| E{Retryable?} E -->|RATE_LIMIT_EXCEEDED| F[Wait + Retry with Backoff] E -->|Other 4xx| G[Show Error to User] D -->|5xx Server Error| H[Retry with Backoff] D -->|Network Error| H F --> I{Max Retries?} H --> I I -->||>= Max| K[Show Error + Support Contact] J --> A ``` Network errors, 5xx server errors, and 429 rate limit responses are safe to retry. Use exponential backoff starting at 1 second and doubling with each attempt (1s, 2s, 4s, 8s), with a maximum of 3-5 retries. Add a small random jitter (0-1 second) to each wait to prevent thundering herd effects when multiple clients retry simultaneously. The `MISSING_REQUIRED_FIELD` error is also retryable after you have collected the required fields from the user. Do not retry authentication errors (401, 403), validation errors (400, 422), not found errors (404), or business logic errors like `OFFER_EXPIRED` and `OFFER_TOKEN_ALREADY_USED`. These require either fixing the request, obtaining fresh data, or informing the user. ## Common Scenarios ### Expired Offer Token When you receive `OFFER_EXPIRED`, the user took too long to complete the booking flow and the token's validity window has passed. Do not retry with the same token. Instead, guide the user back to search with a message like "This offer has expired. Please search again for current availability and pricing." Displaying the `usableUntil` timestamp as a countdown in your UI can help users complete the flow before expiration. ### Offer Token Already Used (Idempotency) {#offer-token-already-used} The `OFFER_TOKEN_ALREADY_USED` error means a booking was already successfully created with this token. This is the API's built-in idempotency mechanism — each prebooked offer token can only produce one booking. > **Important**: This error only occurs if a booking was already created. If previous requests failed with network or server errors, the token is still valid and you can safely retry. This commonly happens when a network timeout occurs during booking creation but the booking actually succeeded on the server, or when the user accidentally submits the form twice. When you receive this error, query the bookings endpoint to retrieve the booking details and show the user their confirmation. Display a message like "A booking has already been created. Please check your bookings or contact support with request ID: `{requestId}`." For handling booking failures in general: retry with the same token on network/server errors (5xx, timeout), check the bookings endpoint on `OFFER_TOKEN_ALREADY_USED`, and obtain a fresh token from search/prebook on `OFFER_EXPIRED` or other errors. ### Offer No Longer Available The `OFFER_EXPIRED` error indicates another user booked the last available option. Show alternative offers from the existing search results if available, or suggest the user search again with nearby locations or adjusted dates. A message like "This option is no longer available. Here are similar alternatives..." provides a good experience. ### Rate Limiting When `RATE_LIMIT_EXCEEDED` is returned, check the `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` response headers to determine when you can retry. If these headers are absent, fall back to exponential backoff. Caching search results locally helps reduce API call volume and avoid hitting rate limits. Show users a message like "Please wait a moment before searching again." ### Additional Information Required The `MISSING_REQUIRED_FIELD` error means a provider needs data that was not part of the initial request. The `error.details.requiredFields` array describes what is needed, with each entry specifying a `field` name, `type` (boolean or string), `label`, and `message`. ```json { "success": false, "error": { "code": "MISSING_REQUIRED_FIELD", "message": "Additional information needed", "details": { "requiredFields": [ { "field": "MULTIPLE_BOOKINGS_CONFIRMATION", "type": "boolean", "label": "Confirm multiple bookings", "message": "More than one booking on one card. Accept?" }, { "field": "FLIGHT_NUMBER", "type": "string", "label": "Flight number", "message": "Please provide your arrival flight number" } ], "retryable": true } } } ``` For boolean fields, display a checkbox or confirmation prompt with the `message`. For string fields, display a text input with the `label`. Once you have collected the information, retry the request with the same `prebookedOfferToken` plus the additional data. The prebook reservation remains valid during this process. Common examples include confirming multiple bookings on a single customer card (Stadtmobil), providing a flight number for airport pickups, or supplying a driver license number for certain vehicle classes. ## Timeout Handling Different operations have different latency characteristics due to the multi-provider architecture: | Operation | Expected Duration | Recommended Timeout | | -------------- | ----------------- | ------------------- | | Search | 1-20 seconds | 25 seconds | | Offer Details | 2-5 seconds | 10 seconds | | Prebook | 3-8 seconds | 15 seconds | | Create Booking | 5-15 seconds | 30 seconds | Show loading indicators with a spinner during long operations, especially searches and booking creation. ## Logging Log every error response together with the `meta.requestId`, the error code and message, what the user was trying to do, and any relevant booking or offer IDs. When contacting support, include the `requestId` along with the error code, the user's action, and a timestamp — this allows us to trace the request through our systems quickly. # Pagination The Mobility API uses two pagination strategies depending on the endpoint type: | Strategy | Used For | Best For | | ---------------- | ------------------------------------------------------------------------ | ----------------------------------------------- | | **Cursor-based** | Search endpoints (cars, stations, micromobility, public-transport stops) | Real-time aggregated results | | **Offset-based** | List endpoints (providers, bookings) | Relatively static or infrequently updated lists | ## Cursor-Based Pagination Cursor-based pagination is used for search endpoints that aggregate results from multiple mobility providers in real-time: - `POST /mobility-api/v2/cars` - `GET /mobility-api/v2/cars/stations` - `POST /mobility-api/v2/micromobility` - `GET /mobility-api/v2/public-transport/stops` _(see note below)_ > **Public-transport stops: text search limitation**: Cursor pagination for `GET /public-transport/stops` is only available when using **coordinate-based search** or supplying an explicit `?provider=` parameter. A plain text query without a provider merges results across all configured providers in a single request and always returns `hasMore: false`. Each cursor encodes an encrypted, server-side **pagination session** that tracks which providers have been queried, how far through each provider's results you are, and the original search parameters. This lets the API query only the providers that still have results on subsequent pages. ### How It Works ```mermaid sequenceDiagram participant Client participant API Client->>API: POST /cars (no cursor) Note over API: Query all providersCreate pagination session API->>Client: {offers: [...], pagination: {hasMore: true, nextCursor: "token_abc"}} Client->>API: POST /cars (cursor: token_abc) Note over API: Resume sessionQuery only providers with more results API->>Client: {offers: [...], pagination: {hasMore: true, nextCursor: "token_def"}} Client->>API: POST /cars (cursor: token_def) Note over API: All providers exhausted API->>Client: {offers: [...], pagination: {hasMore: false}} ``` You send your first request without a cursor. The response includes a `pagination` object with a `hasMore` boolean and, if more results exist, a `nextCursor` string. To fetch the next page, include the `nextCursor` value in your subsequent request. Continue this pattern until `hasMore` is `false`, at which point `nextCursor` will be absent. ### Request Parameters | Parameter | Type | Required | Description | | --------- | ------- | -------- | ---------------------------------------- | | `limit` | integer | No | Results per page (default: 20, max: 100) | | `cursor` | string | No | Pagination cursor from previous response | ### Request and Response Format First page request — no cursor needed: ```bash POST /mobility-api/v2/cars { "pickup": {...}, "dates": {...}, "limit": 20 } ``` Subsequent pages — include cursor from previous response: ```bash POST /mobility-api/v2/cars { "pickup": {...}, "dates": {...}, "limit": 20, "cursor": "token_abc..." } ``` For **GET endpoints** (`/cars/stations`, `/public-transport/stops`), pass `cursor` and `limit` as query parameters: ```bash # First page GET /mobility-api/v2/cars/stations?latitude=48.78&longitude=9.18&radius=1000&limit=20 # Subsequent pages GET /mobility-api/v2/cars/stations?latitude=48.78&longitude=9.18&radius=1000&limit=20&cursor=token_abc... ``` Response format (the `data` key varies per endpoint — `offers`, `stations`, `results`, or `stops`): ```json { "success": true, "data": { "offers": [...], "pagination": { "hasMore": true, "nextCursor": "token_abc..." } } } ``` ### Cursor Behavior Cursors are opaque encrypted strings — never parse, decode, or construct them manually. Store and forward them exactly as received. **Session lifetime:** Pagination sessions expire after 30 minutes of inactivity. Each successful page request resets the 30-minute window. **Parameter changes:** If the search parameters (location, dates, filters) differ from those used to create the cursor, the API silently starts a fresh search and returns first-page results. No error is thrown — you simply receive fresh results. **Provider failures:** If one provider fails during a page request, the session retains that provider's cursor. It will be retried on the next page request. > **Search Consistency**: Use the same search parameters (filters, location, dates) for all pages of a single search. Changing parameters between pages silently starts a fresh search from page 1. ## Offset-Based Pagination Offset-based pagination is used for list endpoints that return database-backed collections: `GET /mobility-api/v2/mobility-providers` and `GET /mobility-api/v2/bookings`. ### How Offset Pagination Works ```mermaid sequenceDiagram participant Client participant API Client->>API: GET /providers?limit=20&page=0 Note over API: Return items 1-20 API->>Client: {items: [...], pagination: {total: 55, currentPage: 0, limit: 20}} Client->>API: GET /providers?limit=20&page=1 Note over API: Return items 21-40 API->>Client: {items: [...], pagination: {total: 55, currentPage: 1, limit: 20}} ``` Specify the `page` number (0-indexed) and `limit` per page. The response includes a `pagination` object with the `total` item count, `currentPage`, and `limit`, which allows you to calculate the total number of pages and determine whether next/previous pages exist. ### Offset Parameters | Parameter | Type | Required | Description | | --------- | ------- | -------- | ---------------------------------------- | | `page` | integer | No | Page number (0-indexed, default: 0) | | `limit` | integer | No | Results per page (default: 20, max: 100) | ### Offset Request and Response ```bash GET /mobility-api/v2/mobility-providers?page=0&limit=20 ``` ```json { "success": true, "data": { "providers": [...], "pagination": { "total": 55, "currentPage": 0, "limit": 20 } } } ``` To calculate total pages: `Math.ceil(total / limit)`. A page beyond the total range returns an empty result set. ## Limitations Cursor-based pagination does not provide a total count because real-time aggregation from multiple providers makes this impossible to determine upfront. Cursors are forward-only — you cannot jump to an arbitrary page or go backwards with the same cursor. If you need "back" functionality, store previously received cursors or start a new search. Offset-based pagination supports random page access but may see slightly degraded performance at very high page numbers on large datasets. # Policies & Terms Every booking in the Adapt2Move API carries cancellation and change policies that govern what actions a traveler can take and under what conditions. These policies are returned alongside offer and booking data, so your integration always has the information it needs to guide users through modifications or cancellations. Policies can shift between the search, prebook, and booking stages. Always rely on the most recent API response for accurate policy data — once a booking is confirmed, the policies attached to that confirmation are final. ## Retrieving Policies Policies appear in two key places during the booking lifecycle. The **prebook response** includes the policies that apply to the offer the user is about to confirm, and the **booking details** response includes the finalized policies for an active booking. Both responses share the same policy structure: ```json { "cancellationPolicy": { "availability": "AVAILABLE_VIA_API", "conditions": [ { "type": "FREE_CANCELLATION", "deadline": "2026-11-30T10:00:00Z", "description": "Free cancellation until 24 hours before pickup" }, { "type": "WITH_FEE", "fee": { "amount": 2500, "currency": "EUR" }, "description": "€25 cancellation fee within 24 hours" } ], "refundPolicy": "FULL_REFUND_IF_BEFORE_DEADLINE" }, "changePolicy": { "availability": "REQUIRES_MANUAL_CONTACT", "conditions": [] } } ``` Each policy contains an `availability` level, an array of time-based `conditions`, and — for cancellations — a `refundPolicy` describing the refund behavior. ## Availability Levels The `availability` field on each policy determines how (or whether) the action can be performed. | Level | Meaning | Implementation | | ------------------------- | --------------------- | ------------------------------------- | | `AVAILABLE_VIA_API` | Can perform via API | Call corresponding API endpoint | | `REQUIRES_MANUAL_CONTACT` | Must contact provider | Display provider contact info to user | | `NOT_AVAILABLE` | Action not allowed | Disable option in UI | > **Check availability before calling**: Always inspect the `availability` field before attempting a cancellation or modification request. Calling an endpoint when the policy is `NOT_AVAILABLE` or `REQUIRES_MANUAL_CONTACT` will result in an error. ## Cancellation Policies Cancellation policies define whether a booking can be cancelled, what it will cost, and by when. The `conditions` array contains one or more time-windowed rules, evaluated in order. The four condition types are: - `FREE_CANCELLATION` — no charge if cancelled before the specified `deadline` - `WITH_FEE` — a fixed fee applies (amount and currency provided in the `fee` object) - `PERCENTAGE_FEE` — a percentage of the total booking price is charged - `NO_REFUND` — the booking is non-refundable regardless of timing Deadlines are ISO 8601 timestamps in UTC. When multiple conditions are present, they typically represent a progression — for example, free cancellation until 48 hours before pickup, then a fee within 48 hours, then no refund after pickup time. Your UI should display the active condition based on the current time relative to these deadlines. ## Change Policies Change policies cover modifications to an existing booking, including date and time adjustments, pickup or dropoff location changes, and traveler detail updates. The structure mirrors cancellation policies, with its own `availability` level and `conditions` array. In practice, change policies tend to be more restrictive than cancellation policies. Name changes are frequently disallowed, location changes may require a new search entirely, and modifications within 24 hours of pickup are often blocked. When the availability is `REQUIRES_MANUAL_CONTACT`, surface the provider's contact information so the user can request changes directly. ## Terms & Conditions Bookings include a reference to the provider's terms and conditions, which the user must accept before confirming a booking. ```json { "termsAndConditions": { "url": "https://provider.com/terms", "mustAcceptBeforeBooking": true } } ``` When `mustAcceptBeforeBooking` is `true`, present a checkbox — such as "I accept the [Terms and Conditions](link)" — and block the booking request until the user has checked it. The terms URL should open in a new tab or modal so the user can review before accepting. Store the acceptance timestamp with the booking record on your side. ## Displaying Policies Clear policy presentation is essential for a trustworthy booking experience. The following items are required integration points: Use plain language when surfacing policy data. Translate condition types into human-readable text — for example, render a `FREE_CANCELLATION` condition with a deadline as "Free cancellation until Nov 30, 10:00 AM" rather than exposing the raw type or ISO timestamp. Place deadline and fee information near the relevant action buttons so users see it at the moment of decision. ## API Endpoints ### Cancel Booking **DELETE** `/mobility-api/v2/cars/bookings/{id}` Check `cancellationPolicy.availability` before calling. If the policy is anything other than `AVAILABLE_VIA_API`, this endpoint will reject the request. ### Modify Booking **PATCH** `/mobility-api/v2/cars/bookings/{id}` Check `changePolicy.availability` before calling. Pass only the fields you want to change in the request body. See [OpenAPI Reference](/mobility-api/v2/docs) for complete request and response schemas. ## Error Handling When a cancellation or modification violates the active policy, the API returns a `POLICY_VIOLATION` error with details about the deadline that was missed: ```json { "success": false, "error": { "code": "POLICY_VIOLATION", "message": "Cancellation not allowed within 24 hours of pickup", "details": { "deadline": "2026-11-30T10:00:00Z", "currentTime": "2026-12-01T08:00:00Z" } } } ``` Handle this gracefully by comparing the deadline and current time from the response, then showing the user a clear explanation — for example, "The cancellation deadline passed on Nov 30 at 10:00 AM." If the change policy allows manual contact, offer the provider's details as a fallback. --- ## Management # Management API The Management API will provide comprehensive tools for organization and tenant administration, compliance settings, travel policies, and mobility provider contract management. > **Note**: Booking management (view, modify, cancel bookings) is already available through the [Mobility API](/docs). ## Whats Planned? Currently, the management of tenants and available providers, API keys and more is only available/controllable through our dashboard. This new API layer should enable our bigger integration partners and customers to control there tenants and give extended possibilities. Even providing new abilities for cross-tenant analytics without compromising data security/privacy. ## Early Access Interested in early access or want to influence feature development? Contact us at [info@adapt2move.de](mailto:info@adapt2move.de) with subject "Management API Early Access" and include your organization's use case and required features.