API Reference

ClawRouter exposes a RESTful API for managing providers, keys, models, and settings programmatically. All endpoints are available at http://localhost:3030.

Version 1.0.18


Provider Management

MethodEndpointDescription
GET/api/providersList all providers with key/today stats
POST/api/providersCreate a new provider. Accepts optional models[] ({id,name} or {model_id,display_name}) to seed the Models tab, and optional default_headers (object of strings)
GET/api/providers/:idGet single provider details
PUT/api/providers/:idUpdate provider settings (default_headers: null clears static headers; system_prompt_rule accepts a rule object or null to clear — see System Prompt Control)
DELETE/api/providers/:idDelete provider (cascades)
PATCH/api/providers/:id/toggleToggle enabled/disabled
PATCH/api/providers/:id/favoriteToggle the favorite flag (Favorites section on the Providers page)

Dashboard Authentication

All /api/* endpoints (except /api/health and /api/auth/*) require a session token from login -- send it as Authorization: Bearer <token>.

MethodEndpointDescription
POST/api/auth/loginVerify the dashboard password ({ password }). Returns { token } (12-hour session)
POST/api/auth/logoutInvalidate the current session token
GET/api/auth/sessionValidate the current session token
POST/api/auth/change-passwordChange the dashboard password ({ currentPassword, newPassword }). Invalidates all sessions

Connection Testing

Every test runs a free /models check followed by a 1-token generation probe (max_tokens: 1) -- the /models 200 alone only proves authentication, not usable credit. Returns { valid, latencyMs, status?, error?, errorType?, softWarning? }. 401/403 and hard billing (402, "insufficient balance" / insufficient_quota -- reported as "recharge required") = invalid; window-quota, transient 429, and gated probe models = valid with a soft warning.

Key-level tests auto-disable a key when the test proves it definitively invalid (!valid + errorType: "AUTH_ERROR" -- bad/expired key or hard billing), via the same record-error path as real traffic; the response gains auto_disabled: true. Never disabled on transient/network/timeout, rate limits, window quota, or content-moderation rejections. Re-testing an already-disabled key reports auto_disabled: true without re-firing the notification. The provider-level test does not disable keys.

MethodEndpointDescription
POST/api/providers/:id/testTest provider connection. Uses the first eligible key in managed mode, keyless otherwise. Accepts optional { "key_value": "..." } to test an unsaved key (test-before-save). Does not disable keys
POST/api/providers/:id/keys/:keyId/testTest one key; persists test_status, test_latency_ms, tested_at, last_test_error on the key row. Auto-disables on a definitive invalid verdict (adds auto_disabled: true)
POST/api/providers/:id/keys/test-allTest all enabled keys sequentially; persists each result, same auto-disable rule per key. Returns { results: [...] } (per-item auto_disabled flag)

API Key Management

MethodEndpointDescription
GET/api/providers/:id/keysList all keys for provider
POST/api/providers/:id/keysAdd a single key (400 if the provider's API Key Mode is none)
POST/api/providers/:id/keys/bulkBulk add keys (newline-separated)
POST/api/providers/:id/keys/bulk-deleteBulk delete keys ({ key_ids }, provider-scoped, max 500 per call). Returns { deleted }
PUT/api/providers/:id/keys/:keyIdUpdate key (label, priority)
DELETE/api/providers/:id/keys/:keyIdDelete key
PATCH/api/providers/:id/keys/:keyId/toggleToggle key enabled/disabled
PATCH/api/providers/:id/keys/:keyId/resetReset key stats to zero
POST/api/providers/:id/keys/reorderReorder key priorities
GET/api/providers/:id/keys/:keyId/errorsGet last 50 errors for key

Provider Fallback Chain

MethodEndpointDescription
GET/api/fallbacksList ALL fallback rows across providers, ordered by provider_id + priority (powers the global Fallback page)
GET/api/providers/:id/fallbacksList fallback entries
POST/api/providers/:id/fallbacksAdd fallback entry (fallback_model_id: null = Automatic)
PUT/api/providers/:id/fallbacks/:fbIdUpdate fallback entry
DELETE/api/providers/:id/fallbacks/:fbIdDelete fallback entry
POST/api/providers/:id/fallbacks/reorderReorder fallback chain

Model Management

MethodEndpointDescription
GET/api/providers/:id/modelsList saved models
POST/api/providers/:id/modelsAdd a model
POST/api/providers/:id/models/bulkBulk add models ({ models: [{ model_id, display_name? }] })
DELETE/api/providers/:id/models/:modelIdDelete a model
POST/api/providers/:id/models/reorderReorder model priorities
POST/api/providers/:id/models/fetchFetch models from upstream. Returns { models, fetched_at, cached }. 5-min in-memory cache; ?force=1 bypasses. Cache invalidated on key add/delete/toggle

Model Circuits & Usage

MethodEndpointDescription
GET/api/providers/:id/model-circuitsOpen model circuits for the provider. Returns [{ model_id, failures, remaining_s, last_error_type }] -- powers the Models-tab "Skipped" badges
GET/api/providers/:id/usageProvider quota/usage probe (Kimi Coding, Z.AI GLM Coding). Uses the first eligible key, falling back to any enabled key when all are quota-backed-off. Returns { supported, membership, region, parallelLimit, disabled, windows: [{ kind, label, limit, used, remaining, resetTime, exhausted }], billing: { usedCents, limitCents, currency, exhausted } | null }. Returns { supported: false } for providers without a known usage endpoint
GET/api/providers/:id/usage?all=1Probes every enabled key in parallel (each key is a separate quota account) -- powers the provider Quota tab. Returns { supported, keys: [{ key_id, label, hint, usage | null, invalid? }] }

Both usage modes auto-disable definitively rejected keys: the rejection runs through the same error classification as real traffic and only an auth-error outcome (401/403, hard-billing codes) disables the key (disable + error history + key_disabled notification). Network failures, transient 5xx, rate limits, window-quota rejections, and no-plan payloads never disable. Z.AI's HTTP-200 {"code":1000,"msg":"Authentication Failed"} dead-key envelope is detected and treated like a real 401.


Virtual Providers (Combos)

MethodEndpointDescription
GET/api/virtual-providersList all combos with members and 24h request stats
POST/api/virtual-providersCreate a combo — { "name", "id"?, "members"? } (name-only create allowed; id must not collide with a provider)
GET/api/virtual-providers/:idCombo detail with members and 24h stats
PUT/api/virtual-providers/:idUpdate name / enabled / replace members (priority defaults to array position)
DELETE/api/virtual-providers/:idDelete the combo (members cascade)
GET/api/virtual-providers/:id /modelsPublic model list — aliases with captured metadata (Prompt for AI source)

Client Rules (System Prompt Control)

Client rules inject/modify the system prompt for requests from a matching client (case-insensitive match against the detected client name; the first enabled rule by priority wins). Rule shape: { "mode": "prepend" | "append" | "replace" | "patch", "text"?, "patches"? } — see the System Prompt Control concept page. Provider and combo rules use the same shape via the system_prompt_rule field on their PUT endpoints.

MethodEndpointDescription
GET/api/client-rulesList all client rules ({ rules: [...] })
POST/api/client-rulesCreate a rule — { "name", "match", "rule", "priority"?, "enabled"? }
PUT/api/client-rules/:idUpdate any subset of fields (rule: null is rejected — a client rule without injection has no effect)
DELETE/api/client-rules/:idDelete the rule

Circuit Breaker

MethodEndpointDescription
GET/api/providers/:id/circuit-statusGet circuit breaker state
POST/api/providers/:id/circuit-resetReset circuit breaker

Notifications

MethodEndpointDescription
GET/api/notificationsList all notifications
POST/api/notifications/:id/readMark as read
DELETE/api/notificationsClear all notifications

Logs

MethodEndpointDescription
GET/api/logsList logs (paginated, filterable)
GET/api/logs/clientsList distinct client names seen in logs (powers the client filter)
GET/api/logs/:idGet single log detail
GET/api/logs/:id/rawGet parsed raw request/response headers and bodies for a log
DELETE/api/logsClear all logs

Global Settings

MethodEndpointDescription
GET/api/settingsGet all global settings
PUT/api/settingsUpdate global settings (includes proxy_auth_enabled -- toggle proxy API key requirement on /proxy/*, default true)

Proxy API Key

MethodEndpointDescription
GET/api/settings/proxy-keyGet the proxy API key and auth state. Returns { key, enabled }
POST/api/settings/proxy-key/regenerateGenerate a new proxy API key. Returns { key, enabled }. The old key stops working immediately

System

MethodEndpointDescription
GET/api/healthHealth check (status, uptime)
GET/api/statsGlobal stats (range: 24h, 7d, all). today.estimated_cost = sum of request costs since local midnight
GET/api/providers/:id/statsProvider-specific stats
GET/api/license-statusActivation/license status
POST/api/check-activationTrigger manual activation check

WebSocket

EndpointDescription
/ws/logsReal-time log updates + notification broadcasts

Proxy

EndpointDescription
POST /proxy/{providerId}/*Main proxy endpoint (all AI requests)

Requests require the proxy API key by default -- sent as Authorization: Bearer <key> (OpenAI style) or x-api-key: <key> (Anthropic style). Requests without a valid key get HTTP 401. The key is shown in the dashboard under Settings > Proxy API Key. The requirement can be toggled via the proxy_auth_enabled global setting (PUT /api/settings).