Admin API
Operator-facing RouteShift endpoints for key lifecycle, usage, routing artifacts, and cache invalidation, with explicit unrestricted and scoped-token boundaries.
The admin API is a separate, operator-facing surface at https://api.routeshift.io/admin. It never accepts a virtual sk-proxy-… key.
Authentication
Authorization: Bearer <operator-admin-token>Admin credentials are provisioned in the RouteShift deployment:
- The unrestricted operator credential can call admin reads and mutations.
- Deployment-configured scoped tokens can call only an allowlist of
GETendpoints and only for an allowedteam_id. - Scoped tokens cannot call admin mutations, even when a matching
team_idis present.
Do not expose an operator admin credential to a client application. Customer traffic belongs on the OpenAI-compatible client API with a virtual key.
Keys
Mint a virtual key
POST /admin/keys
Content-Type: application/json
{
"alias": "prod-agent",
"allowed_models": ["gpt-4o", "gpt-4o-mini"],
"expires_at": "2026-12-31T00:00:00Z",
"rpm": 60,
"tpm": 200000,
"monthly_budget_usd": 100,
"metadata": { "env": "prod", "owner": "agent-team" }
}This mutation requires the unrestricted operator credential. The response includes the one-time sk-proxy-… token. RouteShift stores only its hash; the plaintext is unrecoverable after this response.
List keys
GET /admin/keysWith the unrestricted credential, returns key metadata without the secret. A deployment-configured scoped token can use this read only with an allowed team_id.
Revoke a key
DELETE /admin/keys/{key_id}This mutation requires the unrestricted operator credential. Revocation is immediate. In-flight requests finish; new requests return 401.
Usage
Window query
GET /admin/sessions/window?from=2026-04-01T00:00:00Z&to=2026-04-07T23:59:59Z&limit=500&cursor=…This proxy endpoint requires Authorization: Bearer <ADMIN_SECRET>; the scoped-token allowlist does not include this route. It returns a paginated read of session_metrics; each row contains session ID, team ID, edit/retry turns, one-shot rate, primary model, total cost, and first/last request timestamps. The window cannot exceed seven days.
This endpoint exists primarily to feed the Layer-side yield correlation worker — see Axiom Layer's AI usage tracking.
The response is { sessions, next_cursor }; limit defaults to 200 and is capped at 1,000.
The dashboard's one-shot summary is a separate app route, not an admin-proxy endpoint:
GET /api/usage/one-shot?period=24hCall it on the dashboard origin with a signed-in team member session. It accepts 24h, 7d (default), or 30d and returns summary plus by_model; it does not accept model, from, or to parameters and does not use the proxy admin token.
SSO device flow
Endpoints for the native SSO device-flow key issuance (RFC 8628) — see SSO device flow for the end-to-end story.
Register an IdP connection
POST /admin/idp-configs
Content-Type: application/json
{
"team_id": "team_…",
"provider": "okta",
"login_domain": "acme.com",
"issuer": "https://acme.okta.com",
"client_id": "0oab…",
"client_secret": "…"
}Creates one connection for a (team_id, login_domain) pair. provider is google_workspace or okta. RouteShift validates the issuer's OIDC discovery document at create time and encrypts client_secret at rest.
Errors:
400— invalidprovider, malformedlogin_domain, non-https://issuer, or unreachable discovery document.403—SSO_DEVICE_FLOW_ENABLEDis not set totrueon the deployment.409— thelogin_domainis already registered to another team.
Update an IdP connection
PATCH /admin/idp-configs/{id}?team_id=team_…
Content-Type: application/json
{
"issuer": "https://acme.okta.com",
"client_id": "0oab…",
"client_secret": "…"
}Any subset of issuer, client_id, client_secret can be updated. login_domain and provider are immutable — delete and re-create to change them.
Delete an IdP connection
DELETE /admin/idp-configs/{id}?team_id=team_…Removes the connection. In-flight device authorizations that reference it are cascaded and start failing on their next poll. Existing SSO-issued keys are not revoked — revoke them separately via DELETE /admin/keys/{key_id} if needed.
Device flow (RFC 8628)
Public endpoints called by CLIs and browsers, not with an admin token. Rate limiting and an enabled, provisioned identity-provider connection still apply; the presence of these routes does not guarantee device login is configured for a team.
Start a device authorization
POST /oauth/device/code
Content-Type: application/json
{ "email": "employee@acme.com" }Response:
{
"device_code": "…",
"user_code": "BCDF-GHJK",
"verification_uri": "https://api.routeshift.io/oauth/device/verify",
"verification_uri_complete": "https://api.routeshift.io/oauth/device/verify?user_code=BCDF-GHJK",
"expires_in": 600,
"interval": 5
}Poll for the key
POST /oauth/device/token
Content-Type: application/json
{
"device_code": "…",
"grant_type": "urn:ietf:params:oauth:grant-type:device_code"
}Returns { "access_token": "sk-proxy-…", "token_type": "Bearer", "expires_in": 28800 } once approved. Before approval, returns authorization_pending, slow_down, access_denied, expired_token, or invalid_grant per RFC 8628.
Browser-facing endpoints
GET /oauth/device/verify— HTML page where a signed-in employee approves or denies a pending authorization.POST /oauth/device/verify— same-origin JSON submission from the verify page.Content-Typemust beapplication/json.GET /oauth/device/callback— OIDC redirect target. Register${SSO_CALLBACK_BASE_URL}/oauth/device/callbackin each IdP's app console.
Routing
Invalidate alias cache
POST /admin/model-aliases/invalidateTells the proxy fleet to drop its in-memory model-alias cache. Issued automatically when you save an alias from the dashboard, but exposed here for tooling that edits aliases out-of-band.
Invalidate preset cache
POST /admin/presets/invalidate
Content-Type: application/json
{ "team_id": "team_01H…" }Tells the proxy fleet to drop its in-memory copy of one team's presets so the next request resolves the new body immediately. The dashboard calls this automatically after every preset create, publish, delete, or disable — see Presets → Proxy cache invalidation for how the dashboard surfaces success and failure to API callers.
A non-OK response or network failure does not roll back the database write on the dashboard side; the caller receives proxy_cache_invalidated: false and the proxy re-reads the preset when its per-team TTL (60 seconds) expires.
Response shapes
Success responses are endpoint-specific. Authentication and authorization errors use an error object such as:
{
"error": {
"message": "Unauthorized"
}
}