Developer documentation

Last updated: 27 August 2026

Two ways to integrate with Route AI: deep links (no key, the user's browser does the planning) and the Agent API (key-gated, the server plans and returns structured results with a share link). Both take a plain-language brief in any language.

Open the planner with the brief in the q query parameter (URL-encoded) and the route builds itself on arrival:

https://route-ai.app/?q=90%20km%20coastal%20loop%20from%20Karlskrona%2C%20quiet%20roads

The visitor sees the route drawn on the map and can refine it by chat, drag waypoints, export GPX or a Garmin FIT course, and share it. Good briefs state a start place, a rough distance or duration, the activity, and preferences (loop or one-way, surface, avoid climbs). This is the recommended integration for chat assistants, articles and QR codes.

Agent API — for AI agents and scripts

The Agent API plans server-side and returns structured route data plus a public share link — no browser involved. It exists so that assistants (ChatGPT Actions, Claude/MCP, plain scripts) can complete a request like "plan a 2-day gravel trip around Karlskrona and give me the link" end to end.

Authentication: Authorization: Bearer <token> on every request. The normal way to get a token is the OAuth flow — connect an MCP client (below) and the browser sign-in mints one for you, scoped to your Google account with a daily plan quota. A plan call takes 1–3 minutes: geocoding is throttled to respect OpenStreetMap Nominatim's usage policy.

POST /api/agent/routes — plan a trip

curl -X POST https://route-ai.app/api/agent/routes \
  -H "Authorization: Bearer $KEY" -H "content-type: application/json" \
  -d '{
    "prompt": "Plan a scenic 2-day gravel trip around Karlskrona, coastal and quiet roads",
    "start": "Stena Line Terminal, Karlskrona",
    "end":   "Stena Line Terminal, Karlskrona",
    "days": 2,
    "daily_distance_km": {"min": 80, "max": 100},
    "activity": "cycling",
    "bike_type": "gravel"
  }'

Only prompt is required; the structured fields strengthen the brief when the agent knows them. Also accepted: preferences and avoid (arrays of short strings).

Response (abridged):

{
  "route_id": "8nHYRwAlKSxO",
  "name": "Day 1 +1",
  "days": 2,
  "distance_km": 150.3,
  "elevation_m": 627,
  "stages": [
    {"day": 1, "name": "Eastern coast and archipelago loop",
     "distance_km": 87.8, "elevation_m": 344, "retraced_pct": 33,
     "waypoints": ["Stena Line Terminal, ...", "Sturkö, ...", "..."]},
    {"day": 2, "...": "..."}
  ],
  "share_url": "https://route-ai.app/s/KTB5Mx-yxhLq",
  "export": {"gpx": "https://route-ai.app/api/agent/routes/8nHYRwAlKSxO/export?format=gpx"},
  "planner_reply": "…",
  "warnings": [
    {"type": "OUT_AND_BACK", "message": "'Sturkö' is reached out-and-back on the same road — kept because removing it would fall short of the requested distance."}
  ]
}

Every number is measured by routing real roads (OpenStreetMap + BRouter) — never estimated by a language model. The planner runs one automatic correction pass when a day misses the requested distance range, and reports what it could not fix as warnings rather than hiding it:

warningmeaning
GEOCODE_FAILEDa place name could not be located and was skipped
ROUTING_GAPa stretch could not be routed and is bridged by a straight line
SPUR_REMOVEDa dead-end stop was dropped automatically
OUT_AND_BACKa dead-end stop was kept to honour the requested distance
BACKTRACKINGa share of the day repeats the same roads
UNDER_DISTANCE / OVER_DISTANCEa day misses the requested daily range

GET /api/agent/routes/{route_id}

Returns the same summary. Add ?geometry=1 for full coordinates (large; omitted by default to keep responses small for LLM consumption).

POST /api/agent/routes/{route_id}/share

Returns the public share URL. Idempotent — the link is created with the route and never changes.

GET /api/agent/routes/{route_id}/export?format=gpx

GPX 1.1 with elevation. &day=N exports a single stage. FIT courses are available on the share page; the API is GPX-only for now.

Errors

Errors are structured for machine consumption: {"error": "NO_PLAN", "message": "…", "suggestions": ["…"]}. Codes: UNAUTHORIZED (401), QUOTA_EXCEEDED (429), BAD_REQUEST (400), NO_PLAN / NO_ROUTABLE_DAY (422), ROUTING_FAILED / AI_FAILED (502).

MCP server

Route AI is a hosted MCP server at https://route-ai.app/mcp exposing plan_route, get_route, share_route and export_route. Tools accept either route ids or route-ai.app/s/… share URLs, so "here's my route link — improve day 2" conversations work. One command to connect:

Claude Code

claude mcp add --transport http route-ai https://route-ai.app/mcp

Codex CLI (via the mcp-remote stdio bridge)

codex mcp add route-ai -- npx -y mcp-remote https://route-ai.app/mcp

No key needed: on first use a browser page opens, you sign in with Google and click Approve, and the client receives its own token (OAuth 2.1 with dynamic registration and PKCE). The endpoint is stateless Streamable HTTP, so any other MCP client can connect the same way — or pass Authorization: Bearer rai_… directly for scripts and CI.

Machine-readable discovery

Everything above is discoverable without reading this page:

URLwhat it is
/.well-known/mcp.json MCP manifest: endpoint, tools, auth, install commands
/openapi.json OpenAPI 3.1 — import straight into a ChatGPT Custom GPT Action
/.well-known/oauth-authorization-server OAuth 2.1 metadata (authorize, token, dynamic registration)
/.well-known/oauth-protected-resource Which authorization server protects /mcp
/llms.txt Plain-text summary of the whole service for AI crawlers

Fair use

Planning costs real money and public-API goodwill (OpenAI, Nominatim, BRouter), hence the keys and quotas. Routes and share links are public snapshots — don't send anything private in a prompt. The underlying data is © OpenStreetMap contributors (ODbL); routing by BRouter.