Submit a route request

Updated Sep 16, 2026

POST /api/v1/routes accepts an authenticated route-generation request. Send a valid session; requests without one return 401 Unauthorized.

Each of origin and destination must be either an identifier string, such as "saved:home" or "poi:work", or a coordinate object:

{
  "origin": {"lat": 51.5, "lng": -0.1},
  "destination": "poi:work",
  "preferences": {
    "mode": "walking",
    "themes": ["heritage"],
    "themeIds": ["theme-1"],
    "interestThemeIds": ["interest-1"],
    "avoid": ["busy-roads"],
    "avoidStairs": true
  }
}

Coordinates use WGS84 decimal degrees. Latitude must be between -90 and 90, and longitude between -180 and 180, inclusive. The coordinate object must contain only lat and lng; both values are required. The optional preferences object accepts these fields, all of which are optional:

Field Type Meaning
mode string Routing mode.
themes string[] Theme names.
themeIds string[] Theme identifiers.
interestThemeIds string[] Interest-theme identifiers.
avoid string[] Route features to avoid.
avoidStairs boolean Whether to avoid stairs.

Each preference list (themes, themeIds, interestThemeIds, and avoid) may contain at most 64 entries. Each string preference value, including mode and every list entry, may contain at most 64 UTF-8 bytes. Values or lists exceeding these limits are rejected as 400 Bad Request.

The request body rejects null origin, destination, or preferences, boolean or array locations, coordinate objects with missing or unknown fields, and unknown fields at the top level or in preferences. Omit preferences when no preferences are needed; do not send it as null. These validation failures return 400 Bad Request.

Successful submissions return 202 Accepted with a generated request ID:

{
  "requestId": "<request-id>",
  "status": "accepted"
}

To make retries safe, send an Idempotency-Key header containing a UUID. The first request returns 202 Accepted with status: "accepted". Repeating the same key with the same payload returns 202 Accepted with the original requestId and status: "replayed". Reusing the key with a different payload returns 409 Conflict. An invalid or empty key returns 400 Bad Request; the header is optional. If a backend service required to accept or persist the submission is unavailable, the endpoint returns 503 Service Unavailable.

How long a key stays replayable

An idempotency key is replayable until its record expires. IDEMPOTENCY_TTL_HOURS sets that window on the server, defaulting to 24 hours and clamped to between one hour and ten years, so a value outside that range is brought back to the nearest bound rather than rejected. Sending the same key after the window has passed starts a new request rather than replaying the old response.

The variable, its default, and its clamp are unchanged. Operators need do nothing. Where the server reads it moved in the change that introduced the environment-seam policy, from the domain layer to a configuration adapter; that is an internal Rust API move with no deployment or request-level effect, and the path mapping for in-repository callers is in developers' guide.