API authentication & scopes
How a Routerra Teams API key authenticates over REST and MCP, what read, write and dispatch each unlock, and why a new key has no dispatch scope.
A team key is created in the dashboard under Settings → Developer → API keys, carries the prefix rtk_, and is bound to one team. Every response is scoped to that team: another team's plans, drivers and addresses answer 404, as though they were not there at all.
Creating, disabling and revoking keys is covered in API keys. This article is about what a key can do once you have one.
Sending the key
Over REST there is one way — the X-API-Key header:
curl -H "X-API-Key: rtk_…" \
https://teams-api.routerra.io/api/v1/external/team
The MCP connector accepts three, because not every MCP client can set a custom header:
# header — use this one
X-API-Key: rtk_…
# bearer token
Authorization: Bearer rtk_…
# key in the path, for clients that cannot send custom headers (claude.ai)
POST https://teams-api.routerra.io/mcp/rtk_…
Scopes
Every key carries a set of scopes. READ is mandatory and always present. WRITE and DISPATCH are granted separately, per key.
A call that is short a scope answers 403 insufficient_scope, and the message names both the missing scope and the key, so you can tell which of your keys made the call.
| READ | WRITE | DISPATCH | |
|---|---|---|---|
| GET /team | Yes | No | No |
| GET /drivers | Yes | No | No |
| GET /address-book | Yes | No | No |
| GET /plans | Yes | No | No |
| GET /plans/{id} | Yes | No | No |
| GET /statistics | Yes | No | No |
| GET /changes | Yes | No | No |
| POST …/preview | Yes | No | No |
| POST …/stops | No | Yes | No |
| PUT …/stops/{id} | No | Yes | No |
| POST …/stops/remove | No | Yes | No |
| PATCH /drivers/{id} | No | Yes | No |
| POST …/duplicate | No | Yes | No |
| POST /optimize · dispatch=false | No | Yes | No |
| POST …/changes | No | Yes | No |
| POST /optimize · dispatch=true | No | No | Yes |
| POST …/dispatch | No | No | Yes |
| POST …/approve | No | No | Yes |
| POST …/revoke | No | No | Yes |
| DELETE /plans/{id} | No | No | Yes |
| POST /changes/{id}/apply | No | No | Yes |
Read the table as the minimum scope: because READ is always present, a WRITE key can do everything in the read column too, and a DISPATCH key can do everything in all three.
Two rows are worth a second look:
POST …/previewsits in the read column on purpose. Showing what a destructive operation would do changes nothing and spends no optimization, so any key may ask.POST /optimizeappears twice. The same endpoint needsWRITEwhen it only creates a plan (dispatch=false) andDISPATCHwhen it also sends the plan to the solver.
The same key behaves differently on MCP
Over REST, a missing scope is always a 403. On MCP it isn't. A key that holds write but not dispatch doesn't get an error from dispatch_plan, approve_plan, revoke_plan or cancel_plan — the operation is queued as a proposal for a dispatcher to apply from the plan's pending changes. See Proposed changes.
A key holding only read still gets 403 insufficient_scope on either transport: it cannot even raise a proposal.
For the full list of MCP-versus-REST differences, see MCP connector. For the other reasons a call can be refused, see API error reference.