DurinDoor
Reference

Management API

Every dashboard REST route under /api, which credentials it accepts, and what an API key cannot do.

Dashboard automation uses /api on the same origin as inference. Most management routes accept a DurinDoor API key. Secret reveal, database administration, process control, and local host operations have stricter credentials. See Automating DurinDoor for a workflow and MCP control for JSON-RPC tools.

Credentials

Authentication applies to the decoded route path. Individual handlers can require additional credentials or local access.

CodeWho
sessionDashboard JWT cookie auth_token
CLIHeader x-9r-cli-token (machine-bound)
keyDurinDoor API key: Authorization: Bearer, x-api-key, x-goog-api-key, or ?key=
loopLoopback peer and requireLogin is off
nonePublic. No credential
gwMCP gateway key, not a DurinDoor API key
localLocal-only gate first. Remote callers without CLI get HTTP 403 Local only: CLI token required

Loopback identity comes from wrapper-stamped peer headers, not from Host alone. A request through x-9r-via-proxy is never loopback. Management routes accept session, CLI, key, or loop. Reveal paths reject a key. Always-protected paths (/api/shutdown, /api/settings/database, /api/version/shutdown, /api/version/update, OAuth auto-import) accept session or CLI only. Other protected routes fall through to session, CLI, or loop. POST /api/mcp/control is a separate branch: session, CLI, or key, plus loopback with no credential when requireApiKey is off and Origin is absent or same-origin. Gateway protocol surfaces (/api/mcp-gateway, /sse, /message) take a gateway key, CLI, or loopback, not a DurinDoor API key. Details: MCP control.

A DurinDoor API key is an inference credential. It cannot reveal stored secrets, shut the process down, or change login and proxy settings. Mint a dedicated key for automation and keep reveal, shutdown, and database work on a dashboard session.

Route families

Providers

MethodPathDoesAuth
GET, POST/api/providersList connections (GET) or create an API-key/cookie connection (POST)session, CLI, key, loop
GET/api/providers/clientPaginated connections for the dashboard client, secrets strippedsession, CLI, key, loop
GET/api/providers/kilo/free-modelsCached Kilo free-model catalogsession, CLI, key, loop
PUT/api/providers/reorderAtomically persist a full connection ordersession, CLI, key, loop
GET/api/providers/suggested-modelsSuggested models for a registry fetcher idsession, CLI, key, loop
POST/api/providers/test-batchTest several connections in one callsession, CLI, key, loop
POST/api/providers/validateProbe one provider with a deadline and SSRF guardsession, CLI, key, loop
GET, PUT, PATCH, DELETE/api/providers/{id}Read, replace, patch, or delete one connectionsession, CLI, key, loop
PATCH/api/providers/{id}/auto-pingSet that connection's quota auto-ping flagsession, CLI, key, loop
GET/api/providers/{id}/modelsDiscover models for that connectionsession, CLI, key, loop
POST/api/providers/{id}/testTest one connectionsession, CLI, key, loop
POST/api/providers/{id}/test-modelsPing models by kind using active connection {id}; unavailable pins fail rather than switching accountssession, CLI, open local dashboard (operator only)

Provider nodes

MethodPathDoesAuth
GET, POST/api/provider-nodesList (GET) or create (POST) a compatible/embedding nodesession, CLI, key, loop
POST/api/provider-nodes/validateSSRF-bounded URL probe for a nodesession, CLI, key, loop
PUT, DELETE/api/provider-nodes/{id}Update or delete a nodesession, CLI, key, loop

Media providers

MethodPathDoesAuth
GET, PUT/api/media-providers/routesDefault media routes: GET lists each endpoint's saved order, available models, and effective order; PUT { kind, models } saves one (empty list = automatic). See Media routessession, CLI, key, loop
GET/api/media-providers/tts/deepgram/voicesDeepgram TTS voices grouped by languagesession, CLI, key, loop
GET/api/media-providers/tts/elevenlabs/voicesElevenLabs TTS voices grouped by languagesession, CLI, key, loop
GET/api/media-providers/tts/inworld/voicesInworld TTS voices grouped by languagesession, CLI, key, loop
GET/api/media-providers/tts/minimax/voicesMiniMax TTS voices; query provider and voice_typesession, CLI, key, loop
GET/api/media-providers/tts/voicesShared TTS voice picker (edge-tts, local-device, elevenlabs)session, CLI, key, loop

Keys

MethodPathDoesAuth
GET, POST/api/keysList keys with usage (GET) or mint a key (POST, secret once)session, CLI, key, loop
GET/api/keys/policy-catalogModel ids valid for a key policy allowlistsession, CLI, key, loop
GET/api/keys/usageCurrent usage against each key's windowed limitssession, CLI, key, loop
GET, PUT, DELETE/api/keys/{id}Read, update, or delete a key (secret masked)session, CLI, key, loop
GET/api/keys/{id}/revealReturn the raw DurinDoor key secretsession, CLI, key, loop; key refused on reveal

Key groups

MethodPathDoesAuth
GET, POST/api/key-groupsList or create API-key labels (labels only)session, CLI, loop
GET, PUT, DELETE/api/key-groups/{id}Rename or delete a label; keys staysession, CLI, loop

Combos

MethodPathDoesAuth
GET, POST/api/combos/presetsGET query source=cursor or claude previews missing presets; POST JSON { source } creates only missing names and returns created/skipped countssession, CLI, key, loop
GET, POST/api/combosList combos (GET) or create one (POST, 201)session, CLI, key, loop
GET, PUT, DELETE/api/combos/{id}Read, update, or delete a combosession, CLI, key, loop

Connection groups

MethodPathDoesAuth
GET, POST/api/connection-groupsList or create connection groupssession, CLI, key, loop
GET, PUT, DELETE/api/connection-groups/{id}Read, update, or delete a groupsession, CLI, key, loop
GET, POST, DELETE/api/connection-groups/{id}/membersList, add, or remove group memberssession, CLI, key, loop

Models

MethodPathDoesAuth
GET, POST/api/models/auto-syncGET optional provider query returns roster/status and pruned references; POST optional JSON { provider } forces eligible provider sync or all enabled providerssession, CLI, key, loop
GET, PUT/api/modelsDashboard model list with aliases (GET); set one alias (PUT)session, CLI, key, loop
GET, PUT, DELETE/api/models/aliasList, set, or delete model aliasessession, CLI, key, loop
GET, POST/api/models/availabilityList model cooldowns (GET); POST clearCooldownsession, CLI, key, loop
GET, POST, PATCH, DELETE/api/models/customCRUD custom model rows and capabilitiessession, CLI, key, loop
GET, POST, DELETE/api/models/disabledList, set, or clear disabled model ids per providersession, CLI, key, loop
GET, PUT, DELETE/api/models/enabledList, set, or clear the visible-model allowlist per providersession, CLI, key, loop
POST/api/models/testPing one model by kind. Optional connectionId pins the probe to an active account; unavailable pins fail without switching accounts. API keys cannot request a pin because internal probes use an unscoped CLI tokenunpinned: session, CLI, key, loop; pinned: session, CLI, open local dashboard (operator only)
POST/api/models/test/batchPing up to 200 models; streamed results. Optional connectionId pins each probe to an active account with the same operator restrictionunpinned: session, CLI, key, loop; pinned: session, CLI, open local dashboard (operator only)

Pricing

MethodPathDoesAuth
GET, PATCH, DELETE/api/pricingRead, patch, or reset merged pricingsession, CLI, key, loop

Tags

MethodPathDoesAuth
OPTIONS, GET/api/tagsOllama-shaped tag list used by local clientssession, CLI, key, loop

Usage

MethodPathDoesAuth
GET/api/monitoringRuntime status, 24-hour activity, and 7-day provider health from recorded usagesession, CLI, key, loop
POST/api/usage/{connectionId}/claude-resetJSON { grantId } consumes one Claude OAuth reset grant; irreversible, not a read-only quota probesession, CLI, key, loop
GET/api/usage/chartUsage chart buckets for a periodsession, CLI, key, loop
GET/api/usage/combosCombo-attributed usage for a periodsession, CLI, key, loop
GET/api/usage/historyPaginated usage rows (limit up to 200, offset, filters)session, CLI, key, loop
GET/api/usage/logsLast 200 usage log rowssession, CLI, key, loop
GET/api/usage/meSelf-service usage for the calling API key only: lifetime totals, daily limit status, policy, and quota snapshots for the key's own connections. ?format=json is accepted; requires a resolvable application API key, not just session/CLI/loopkey
GET/api/usage/providersDistinct providers from request detailssession, CLI, key, loop
GET/api/usage/request-detailsPaginated request-detail rowssession, CLI, key, loop
GET/api/usage/request-logsLast 200 usage log rows (alias of logs)session, CLI, key, loop
POST/api/usage/resetDelete usage history for a periodsession, CLI, key, loop
GET/api/usage/statsAggregate usage for a period or date rangesession, CLI, key, loop
GET/api/usage/streamSSE of full stats for a dashboard periodsession, CLI, key, loop
GET/api/usage/{connectionId}Live provider quota/usage for one connection; ?force=1session, CLI, key, loop
GET, POST/api/usage/{connectionId}/codex-reset-creditsRead or trigger Codex credit resetsession, CLI, key, loop

Logs

MethodPathDoesAuth
GET/api/logs/exportStreamed JSON export of requestDetails rows. ?hours=1-168 (default 24), ?limit=1-50000 (default 10000). Truncated exports carry capped, limit, and totalAvailable in the response header, ahead of the row array, so a streaming reader learns about truncation before it finishes the bodysession, CLI, loop

Timeline

MethodPathDoesAuth
GET, DELETE/api/timelinePaginated sidecar traces (GET); wipe traces (DELETE)session, CLI, key, loop
GET/api/timeline/streamSSE of live sidecar writessession, CLI, key, loop
GET/api/timeline/{id}One sidecar trace plus eventssession, CLI, key, loop
GET/api/timeline/{id}/metaOne trace's metadata without event payloadssession, CLI, key, loop

Settings

MethodPathDoesAuth
GET, PATCH/api/settingsRead settings (GET) or patch them (PATCH), including the mediaRoutes key (400 Invalid mediaRoutes on a malformed value)session, CLI, key, loop
GET, POST/api/settings/auto-configureHeadroom/PxPipe/Firecrawl probe status or runsession, CLI, key, loop; handler wants session
GET, POST/api/settings/databaseExport/import the data file; JWT or CLI plus passwordsession, CLI; dashboard password required
POST/api/settings/database/cutoverRun SQLite to PostgreSQL cutover; 503 + Retry-After: 5 if the lock is heldsession, CLI; dashboard password required
GET, POST/api/settings/database/engineRead engine status (servingFallback; never postgresUrl) or patch version/featuressession, CLI; password
GET/api/settings/database/logRecent pgCutoverLog rowssession, CLI; dashboard password required
POST/api/settings/database/rollbackRestore an allowlisted cutover snapshot; 409 rollback_requires_force unless force: truesession, CLI; password
POST/api/settings/database/selectiveSelective provider/combo transfer; preview is secret-freesession, CLI; dashboard password required
POST/api/settings/database/testProbe a candidate Postgres URL without opening the adaptersession, CLI; dashboard password required
POST/api/settings/firecrawl/detectProbe Firecrawl endpoints; session or open dashboard in handlersession, CLI, key, loop; handler wants session
POST/api/settings/proxy-testTest the configured outbound proxy against a fixed URLsession, CLI, key, loop
GET/api/settings/require-loginPublic requireLogin and tunnel access flagsnone

Database routes require a dashboard session or CLI token plus the dashboard password. Most engine, cutover, rollback, log, and test calls read the password from x-9r-password. Selective transfer reads JSON password; database export uses x-9r-password, and import reads JSON password. Database JSON imports are limited to 16 MiB.

Data retention

MethodPathDoesAuth
GET, POST/api/data-retentionRead retention policy (GET); run sweep (POST, JWT/CLI)session, CLI, loop; POST needs JWT/CLI

MCP

MethodPathDoesAuth
POST/api/mcp/controlJSON-RPC 2.0 management MCP serversession, CLI, key; loopback if requireApiKey off
POST/api/mcp/{plugin}/messageJSON-RPC companion for a local stdio MCP plugin SSE sessionsession, CLI, key, loop; local
GET/api/mcp/{plugin}/sseSSE handshake for a local stdio MCP plugin bridgesession, CLI, key, loop; local

MCP gateway

MethodPathDoesAuth
POST, GET/api/mcp-gatewayGateway JSON-RPC POST; GET is 405gw, CLI, loopback
GET, POST/api/mcp-gateway/instancesList or create upstream MCP instancessession, CLI, key, loop
GET, PUT, DELETE/api/mcp-gateway/instances/{id}Read, update, or delete an instancesession, CLI, key, loop
POST/api/mcp-gateway/instances/{id}/testList upstream tools as a probesession, CLI, key, loop
GET, POST/api/mcp-gateway/keysList gateway keys (GET); create is local-only (POST 201)session, CLI, key, loop
GET, PUT, DELETE/api/mcp-gateway/keys/{id}Read (optional ?reveal=1), update grants, or deletesession, CLI, key, loop
GET/api/mcp-gateway/keys/{id}/revealRaw gateway secret; local request onlysession, CLI, key, loop; key refused on reveal
POST/api/mcp-gateway/messageSSE companion POST; needs sessionIdgw, CLI, loopback
GET/api/mcp-gateway/oauth/{id}/{action}authorize, callback, status, or client-metadatamixed (see oauth leaves)
GET/api/mcp-gateway/sseSSE handshake; returns the message URLgw, CLI, loopback

Proxy pools

MethodPathDoesAuth
GET, POST/api/proxy-poolsList or create proxy poolssession, CLI, key, loop
POST/api/proxy-pools/cloudflare-deployDeploy a Cloudflare relay workersession, CLI, key, loop
POST/api/proxy-pools/vercel-deployDeploy a Vercel relay functionsession, CLI, key, loop
GET, PUT, DELETE/api/proxy-pools/{id}Read, update, or delete a poolsession, CLI, key, loop
POST/api/proxy-pools/{id}/testTest one pool entrysession, CLI, key, loop

Tunnel

MethodPathDoesAuth
POST/api/tunnel/disableStop the Cloudflare tunnelsession, CLI, key, loop; local
POST/api/tunnel/enableStart the Cloudflare tunnelsession, CLI, key, loop; local
GET/api/tunnel/statusTunnel and Tailscale statussession, CLI, key, loop
GET/api/tunnel/tailscale-checkParallel Tailscale probessession, CLI, key, loop; local
POST/api/tunnel/tailscale-disableDisable Tailscale servesession, CLI, key, loop; local
POST/api/tunnel/tailscale-enableEnable Tailscale servesession, CLI, key, loop; local
POST/api/tunnel/tailscale-installInstall Tailscale on this hostsession, CLI, key, loop; local

CLI tools

MethodPathDoesAuth
GET, POST, DELETE/api/cli-tools/codewhale-settingsRead, apply, or reset codewhale client settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/crush-settingsRead, apply, or reset crush client settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/forge-settingsRead, apply, or reset forge client settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/pi-settingsRead, apply, or reset pi client settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/smelt-settingsRead, apply, or reset smelt client settingssession, CLI, key, loop
GET/api/cli-tools/all-statusesBatch CLI-tool install/config statussession, CLI, key, loop
GET, POST, DELETE, PATCH/api/cli-tools/antigravity-mitmMITM status and mutations for Antigravitysession, CLI, key, loop; local
GET, PUT/api/cli-tools/antigravity-mitm/aliasRead or save MITM aliases for a toolsession, CLI, key, loop; local
GET, POST, DELETE/api/cli-tools/claude-settingsRead, apply, or reset Claude Code settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/cline-settingsRead, apply, or reset Cline settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/codex-settingsRead, apply, or reset Codex settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/copilot-settingsRead, apply, or reset Copilot settingssession, CLI, key, loop
GET/api/cli-tools/cowork-mcp-registryFiltered Cowork MCP registry listsession, CLI, key, loop
POST/api/cli-tools/cowork-mcp-toolsProbe a Cowork MCP server tools/listsession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/cowork-settingsRead, apply, or reset Cowork settingssession, CLI, key, loop; local
GET, POST, DELETE/api/cli-tools/deepseek-tui-settingsRead, apply, or reset DeepSeek TUI settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/droid-settingsRead, apply, or reset Droid settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/grok-build-settingsRead, apply, or reset Grok Build config.tomlsession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/hermes-settingsRead, apply, or reset Hermes settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/jcode-settingsRead, apply, or reset JCode settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/kilo-settingsRead, apply, or reset Kilo settingssession, CLI, key, loop
GET, POST, DELETE/api/cli-tools/openclaw-settingsRead, apply, or reset OpenClaw settingssession, CLI, key, loop
GET, POST, PATCH, DELETE/api/cli-tools/opencode-settingsRead, apply, patch, or reset OpenCode settingssession, CLI, key, loop

Translator

MethodPathDoesAuth
GET, DELETE/api/translator/console-logsRead or clear translator console logssession, CLI, key, loop
GET/api/translator/console-logs/streamSSE of translator console logssession, CLI, key, loop
GET/api/translator/loadLoad an allowlisted translator fixture filesession, CLI, key, loop
POST/api/translator/saveSave an allowlisted translator fixture filesession, CLI, key, loop
POST/api/translator/sendSend a translator playground requestsession, CLI, key, loop
POST/api/translator/translateRun a one-shot translation without sending upstreamsession, CLI, key, loop

OAuth

MethodPathDoesAuth
POST/api/oauth/xiaomi-mimo/api-keySave apiKey or mimoPassToken; optional uid, baseUrl, mimoUserId, mimoCUserId, region. Key must start sk-; base URL must use HTTPS on a Xiaomi MiMo hostsession, CLI, key, loop
GET/api/oauth/xiaomi-mimo/auto-importImport local MiMo Desktop credentialssession, CLI; local
POST/api/oauth/xiaomi-mimo/login/startBegin browser login; optional JSON region, default cnsession, CLI, key, loop
GET/api/oauth/xiaomi-mimo/login/statusPoll login-session cookie; optional matching state; returns pending/done or 404 expiredsession, CLI, key, loop
POST/api/oauth/codex/bulk-importBulk-import Codex OAuth account JSONsession, CLI, key, loop
POST/api/oauth/codex/import-tokenImport a ChatGPT access token as a connectionsession, CLI, key, loop
GET/api/oauth/cursor/auto-importRead Cursor tokens from local SQLitesession, CLI; local
POST, GET/api/oauth/cursor/importImport a Cursor access tokensession, CLI, key, loop
POST/api/oauth/gitlab/patCreate a GitLab Duo connection from a PATsession, CLI, key, loop
POST/api/oauth/grok-cli/bulk-importBulk-import Grok CLI device accountssession, CLI, key, loop
POST/api/oauth/iflow/cookieCreate an iFlow connection from a cookiesession, CLI, key, loop
POST/api/oauth/kiro/api-keyImport a Kiro headless API keysession, CLI, key, loop
GET/api/oauth/kiro/auto-importRead Kiro refresh tokens from the AWS SSO cachesession, CLI; local
POST/api/oauth/kiro/importImport a Kiro IDE refresh tokensession, CLI, key, loop
POST/api/oauth/kiro/import-cli-proxyImport Kiro CLIProxyAPI auth JSONsession, CLI, key, loop
GET, POST/api/oauth/kiro/social-authorizeStart Kiro social OAuth (POST; GET kept)session, CLI, key, loop
POST/api/oauth/kiro/social-exchangeExchange a Kiro social codesession, CLI, key, loop
GET, POST/api/oauth/{provider}/{action}Generic OAuth: authorize, device-code, exchange, poll, cancel, proxies, import-token, manual-codesession, CLI, key, loop

Compression

MethodPathDoesAuth
POST/api/compression/previewRun catalog engines on a body and report per-id statussession, CLI, loop

Token saver

MethodPathDoesAuth
GET/api/token-saver/streamSSE of token-saver aggregates for a periodsession, CLI, loop

Headroom

MethodPathDoesAuth
GET, POST/api/headroom/extrasList or install Headroom extra wheelssession, CLI; local
GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS/api/headroom/proxy/{path...}Reverse-proxy into the Headroom processsession, CLI; local
POST/api/headroom/startStart the managed Headroom venv processsession, CLI; local
GET/api/headroom/statsHeadroom circuit and usage statssession, CLI, key, loop
GET/api/headroom/statusHeadroom process status; URL userinfo redacted for a keysession, CLI, key, loop
POST/api/headroom/stopStop the managed Headroom processsession, CLI; local

PxPipe

MethodPathDoesAuth
POST, GET/api/pxpipe/healthPxPipe module health; GET mirrors POSTsession, CLI; loop follows requireLogin
GET/api/pxpipe/logsPxPipe module logssession, CLI; loop follows requireLogin
POST/api/pxpipe/restartReload the in-process PxPipe modulesession, CLI; loop follows requireLogin
POST/api/pxpipe/startWarm the in-process PxPipe transform modulesession, CLI; loop follows requireLogin
GET/api/pxpipe/statsPxPipe transform statssession, CLI; loop follows requireLogin
GET/api/pxpipe/statusPxPipe module statussession, CLI; loop follows requireLogin
POST/api/pxpipe/stopDrop the in-process module (fail-open passthrough)session, CLI; loop follows requireLogin

Health

MethodPathDoesAuth
GET, HEAD, OPTIONS/api/livezProcess-alive response; protected by the generic dashboard gatesession, CLI, loop
GET, OPTIONS/api/readyzAlias of health response; protected by the generic dashboard gatesession, CLI, loop
GET, OPTIONS/api/healthLiveness { ok: true } with CORSnone
GET, DELETE, OPTIONS/api/health/providersCached provider health; DELETE invalidates; ?force=1none

Version

MethodPathDoesAuth
GET/api/versionInstalled version and npm latestnone
POST/api/version/shutdownExit so a manual update can take file lockssession, CLI
POST/api/version/updateSchedule the detached updater and exitsession, CLI

Locale

MethodPathDoesAuth
POST/api/localeSet the locale cookienone

Init

MethodPathDoesAuth
GET/api/initBoot initializer; body is the string Initializednone

Auth

MethodPathDoesAuth
POST/api/auth/mfa/setupJSON { password }; returns candidate secret, otpauthUri, and qrCodeDataUri; persists nothingsession, CLI, loop; same-origin and password
POST/api/auth/mfa/enableJSON { password, secret, code }; verifies candidate TOTP, enables MFA, returns backup codes oncesession, CLI, loop; same-origin and password
POST/api/auth/mfa/disableJSON { password, code }; requires password and TOTP or backup codesession, CLI, loop; same-origin and second factor
POST/api/auth/mfa/verifyJSON { code }; completes password login using the short-lived mfa_pending cookienone at route gate; pending cookie and same-origin required
POST/api/auth/change-passwordOne-shot default-password change with a loopback proofnone
POST/api/auth/loginPassword login; issues auth_tokennone
POST/api/auth/logoutClear the session cookienone
GET/api/auth/oidc/callbackOIDC redirect callbacknone
GET/api/auth/oidc/startStart the OIDC login redirectnone
POST/api/auth/oidc/testProbe OIDC discovery (400 on SSRF/URL errors)none
POST/api/auth/reset-passwordClear the stored hash back to the default (local-only)session, CLI; local
GET/api/auth/statusSession state and whether the built-in password is activenone

Shutdown

MethodPathDoesAuth
POST/api/shutdownDev-only process exit; needs SHUTDOWN_SECRET; 403 in productionsession, CLI

What an API key cannot do

The gate refuses a key on GET /api/keys/{id}/reveal, GET /api/mcp-gateway/keys/{id}/reveal (also 403 unless loopback), any ?reveal=1 (including an encoded /reveal path), /api/shutdown, /api/version/shutdown, /api/version/update, /api/settings/database and children, /api/oauth/cursor/auto-import, /api/oauth/kiro/auto-import, /api/oauth/xiaomi-mimo/auto-import, and remote calls to local-only spawn paths (tunnel enable/disable, Tailscale, Cowork settings, Antigravity MITM, Headroom start/stop/proxy/extras, MCP plugin /sse and /message, POST /api/auth/reset-password). Families that do not accept an inference key: /api/key-groups, /api/data-retention, /api/compression, /api/token-saver, /api/pxpipe.

After the gate, GET/POST /api/settings/auto-configure and POST /api/settings/firecrawl/detect still want a session (or requireLogin off). POST /api/data-retention needs JWT or CLI. POST /api/mcp-gateway/keys is local-only in the handler. POST /api/shutdown also needs SHUTDOWN_SECRET and returns 403 in production.

Gateway OAuth leaves: client-metadata is public. callback takes session, CLI, or loop. authorize and status sit on the management gate, so a key can finish a connect flow.

Settings keys stripped from an API-key patch

PATCH /api/settings always drops password, passwordSessionEpoch, mitmSudoEncrypted, postgresUrl, mfaEnabled, mfaSecret, and mfaBackupCodes. GET /api/settings withholds postgresUrl, mfaSecret, and mfaBackupCodes from every caller, including an operator session: the libpq URL carries user:password@. Without a session or CLI it also drops these login and proxy settings: exposeComboOnly, requireLogin, requireApiKey, authMode, oidcIssuerUrl, oidcClientId, oidcClientSecret, oidcScopes, oidcLoginLabel, tunnelDashboardAccess, enableObservability, outboundProxyEnabled, outboundProxyUrl, outboundNoProxy, claudeClassifierCompat. claudeAutoPing and codexAutoPing return 400 (Auto-ping must be updated through the connection-scoped endpoint). MCP update_settings strips the same secret set and auth-critical set plus those auto-ping keys, and returns 400 No updatable settings provided when nothing remains. POST /api/settings/database/engine also strips postgresUrl, password, passwordSessionEpoch, oidcClientSecret, and mitmSudoEncrypted from the returned settings object. POST /api/settings/database/rollback accepts { snapshotPath?, force } and returns { error: "rollback_requires_force", warning, discardedSince } without force: true. POST /api/settings/database/cutover returns { error: "A cutover is already in flight; retry in a moment." } when the lock is held.

Proxy URL redaction

An API key sees host and port. user:password@ becomes ***. Unparsable strings become ***. Session, CLI, and an open local dashboard keep the raw value so an edit form does not persist the placeholder. Fields: outboundProxyUrl on settings, proxyUrl on proxy pools, providerSpecificData.connectionProxyUrl on providers, and headroomUrl on GET /api/headroom/status. MCP get_settings redacts outboundProxyUrl. MCP list_connections drops connectionProxyUrl instead of redacting it.

Errors

HTTPBodyWhen
401{ error: "Unauthorized" }Gate: missing or wrong credential, including control with a foreign Origin
401{ error: "gateway key required" }Gateway protocol surface without a gateway key (and not loopback/CLI)
403{ error: "Local only: CLI token required" }Local-only path from a remote peer without CLI
403{ error: "Key creation is only available from local requests." }Remote POST /api/mcp-gateway/keys
403{ error: "Key reveal is only available from local requests." }Remote gateway-key reveal
400{ error: "<message>" }Validation (combo name, missing provider, bad period)
404{ error: "Connection not found" } / { error: "Combo not found" } / { error: "Key not found" }Missing row
409provider-connection conflict; { error: "rollback_requires_force" }Duplicate API-key connection create; rollback after cutover without force: true
500{ error: "Failed to ..." }Handler catch-all

Malformed percent-encoding on an /api path is 401, fail-closed. Combo create and key create are 201. Key create includes key; later list/detail return maskedKey.

Request examples

Create a fallback combo (letters, digits, -, _, .; duplicates are 400; optional members, kind, capabilities, allowedConnectionIds up to 500):

curl http://localhost:20128/api/combos \
  -H "Authorization: Bearer YOUR_DURINDOOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"coding-default","models":["anthropic/claude-sonnet-4-5","openai/gpt-4.1"]}'

Add an API-key connection and read quota. POST needs provider and name. apiKey is required unless the provider is noAuth or ollama-local. OAuth accounts use /api/oauth/.... GET /api/usage/{connectionId} returns provider quota for OAuth or a usage-eligible API-key provider. ?force=1 skips that provider's in-process cache. MCP snapshot refresh is refresh_quota.

curl http://localhost:20128/api/providers \
  -H "Authorization: Bearer YOUR_DURINDOOR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider":"openai","name":"openai-main","apiKey":"YOUR_UPSTREAM_API_KEY"}'

curl "http://localhost:20128/api/usage/CONNECTION_ID?force=1" \
  -H "Authorization: Bearer YOUR_DURINDOOR_API_KEY"

On this page

Edit on GitHub