Conventions
These rules hold for every endpoint.
Data types
Section titled “Data types”| Type | Format |
|---|---|
| Field names | snake_case, English |
| Identifiers | Strings. Treat them as opaque |
| Timestamps | ISO 8601 in UTC, for example 2026-11-09T08:14:03Z |
| Money | { "value": "12.50", "currency": "EUR" }, with the value as a string with two decimals |
| Enumerations | English values, see the glossary. Codes your company defines, such as ride types and outcomes, are returned as stored |
| Absent values | null |
Pagination
Section titled “Pagination”List endpoints return a page and a cursor:
{ "data": [ … ], "next_cursor": "WzE3MzEx…" }Pass next_cursor as cursor to get the next page. It’s null on the last page. limit sets the page size: 25 by default, at most 100.
Filtering and incremental sync
Section titled “Filtering and incremental sync”Every list accepts created_after, created_before and updated_after, plus filters of its own, such as status on rides or phone on contacts.
To keep a copy in sync, ask for updated_after your last sync time minus 60 seconds, and deduplicate by id. A write committed just before your read can carry an earlier timestamp, and the overlap catches it.
Deleted records don’t show up in updated_after. Read them from GET /v1/events, or receive them as webhooks.
Idempotency
Section titled “Idempotency”Send an Idempotency-Key header with every POST: any unique string, such as a UUID. Ambula keeps the key for 24 hours.
- A retry with the same key and body returns the first response, so a timeout never plans a ride twice.
- The same key with a different body returns
422. - A key whose first request is still running returns
409 request_in_progress.
Concurrency
Section titled “Concurrency”A single-record GET returns an ETag header. Send it as If-Match with PATCH or DELETE. If the record changed since you read it, you get 412 Precondition Failed and nothing changes: dispatchers, drivers and API clients all edit the same rides.
Rate limits
Section titled “Rate limits”Limits apply per key. By default:
| Limit | Default |
|---|---|
| Reads | 120 a minute |
| Writes | 60 a minute |
| Records read | 200,000 a day |
| Chip lookups and questions | A separate daily quota |
Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. A 429 adds Retry-After, in seconds. Failed authentication is limited per IP address.
Request ids
Section titled “Request ids”Send an X-Request-Id and Ambula echoes it, or generates one, on every response and in every error body. Quote it when you contact support.
Originals of photos and videos come as signed URLs valid for 15 minutes; list them again for fresh ones. Thumbnails are served by GET /v1/media/{id}/thumbnail and need your key. API keys never go in URLs.
The production API is server-to-server and sends no CORS headers. The sandbox allows browser requests from this documentation only.