Bifrost

Admin API reference

Every admin route, grouped by area. Exact request/response shapes are in the OpenAPI spec.

All /admin/* routes require an operator identity: Authorization: Bearer $MASTER_KEY, or — when DASH_ENABLED — an operator session cookie whose role carries the permission that route needs (the master key carries all of them). See Security → Roles. Success responses use { "data": ... }; paginated lists use { "data": [...], "pagination": { limit, offset, total, nextOffset } }.

Any POST /admin/* accepts an optional Idempotency-Key header — see Idempotent creates. This page is a map of what exists; for exact request/response bodies, see apps/gateway/openapi.yaml or the interactive spec (see API guide).

Typed clients

Every management response carries a schema in apps/gateway/openapi.yaml, so a fully typed client is generated rather than hand-written:

bunx openapi-typescript apps/gateway/openapi.yaml -o src/api/schema.d.ts

Pair it with a typed fetch wrapper (openapi-fetch or equivalent) and paths, query parameters, request bodies and response shapes are all checked at compile time.

The spec is generated from the same Zod schemas the API validates with, and two tests fail the build if it drifts: one when the committed YAML is stale, and one when a management response is documented without a schema. There is no second, private protocol for our own UI to use — the documented API is the API.

Deployments

RoutePurpose
GET /admin/operationsAvailable operations, required credentials, and transports per adapter.
POST /admin/deployments/resolveDry-run: resolve effective capabilities/operations/transports for a body, without saving.
GET /admin/deploymentsList deployments. Query: enabled, publicModel, q (search name/prefix).
POST /admin/deploymentsCreate a deployment (see Creating deployments).
GET /admin/deployments/:idRead one deployment.
PATCH /admin/deployments/:idUpdate a deployment.
DELETE /admin/deployments/:idDelete a deployment.

Virtual keys

RoutePurpose
GET /admin/keysList keys. Query: limit, offset, enabled, publicModel, q (search name/prefix).
POST /admin/keysCreate a virtual key (see Virtual keys).
PATCH /admin/keys/:idUpdate scope, limits, budget, or enabled state; send null to clear a limit.
DELETE /admin/keys/:idPermanently delete a key.

Router and fallbacks

RoutePurpose
GET /admin/router-settingsEffective router configuration (defaults if never set).
PUT /admin/router-settingsPatch any subset of routing/parameter-policy/retry settings — see Routing and Parameter policy.
GET /admin/fallbacksList fallback chains.
PUT /admin/fallbacksUpsert a chain (primaryModel, fallbackModels, optional reason).
DELETE /admin/fallbacks/:primaryModel/:reasonRemove a chain. See Fallbacks.

Cache

RoutePurpose
DELETE /admin/cacheClear cached entries. Query: callType, namespace (a virtual key id) — omit both to clear everything. See Caching.

Logs and usage

RoutePurpose
GET /admin/logsPaginated operation summaries. Filters include outcome, degraded, active, terminalVerified, failureKind, failurePhase, duration, model, key, request id, cache, and time range.
GET /admin/logs/:operationIdFull operation record, ordered upstream-attempt timeline, and whether a retained sample exists and can be read (payload.retained, payload.readable, payload.access).
GET /admin/logs/:operationId/payloadDecrypt the retained forensic sample kept for every finished operation. Requires payloads:read (master, owner, or admin) and is audit logged, outcome included. 403 payload_access_sealed when the deployment runs OBSERVABILITY_PAYLOAD_ACCESS=sealed, 404 once retention has swept the sample, 409 when it outlived its key.
GET /admin/observability/summaryOutcomes, degraded/stalled/active/abandoned counts, retries, and latency percentiles. Takes a trailing `window=5m
GET /admin/usageConsumer usage/cost and estimated upstream cost across every attempt; group by public_model, virtual_key, actor, hour, day, or none. Filterable by actor. See Cost accounting.

Audit trail

RoutePurpose
GET /admin/auditPaginated, newest first. Merges the record of mutating /admin calls with the record of retained-payload reads. Query: kind (admin, payload_access), actor, action, targetType, start, end. Requires audit:read (master key or owner).

Both tables are read and interleaved, so offset + limit is capped at 5000 entries: older ones are reached with a date range or a narrower filter rather than a deeper page. Configuration entries are kept for ADMIN_AUDIT_RETENTION_DAYS (365 by default) and payload-read entries for as long as the operation metadata they point at.

Reads are not audited — they are the overwhelming majority of dashboard traffic and would bury the entries that matter. The deliberate exception is GET /admin/logs/:id/payload: it is a read, but it reveals real prompts and completions, so it lands here as a payload_access entry. The trail is append-only; there is no write or delete route for it.

Idempotent creates

Send Idempotency-Key: <opaque string> with a POST and the gateway remembers what that key produced. Retrying with the same key returns the original response — same status, same body, plus Idempotent-Replay: true — instead of creating a second resource. It is opt-in: a request without the header behaves exactly as it always has.

  • Keys are scoped to the caller and expire after 24 hours.
  • Reusing a key with a different body is 409 idempotency_key_reuse, because that is a client bug rather than a retry.
  • A retry that arrives while the first request is still running is 409 idempotency_key_in_progress.
  • Only successful responses are remembered. A rejected or failed call releases the key, so the corrected request can reuse it.

Operators and sessions

Present only when DASH_ENABLED. /auth/* is deliberately mounted outside /admin: every /admin route requires an already-resolved operator identity, so the route that creates one cannot live under it.

RoutePurpose
GET /auth/configUnauthenticated probe: whether human authentication exists here. Always present.
POST /auth/sessionLog in. Sets the httpOnly session cookie and the readable CSRF cookie. Rate limited per username and per IP.
GET /auth/sessionCurrent identity and the permissions its role grants.
DELETE /auth/sessionLog out; revokes the session server-side and clears both cookies.
POST /auth/passwordChange your own password. Revokes your other sessions and reissues the current one. Not available to the root operator.
GET /admin/usersList operators. Query: limit, offset, role, q. Owner only.
POST /admin/usersCreate an operator. The only way an account comes into existence. Owner only.
GET /admin/users/:idRead one operator. Owner only.
PATCH /admin/users/:idChange role or enabled state. Either one revokes that user's live sessions. Owner only.
POST /admin/users/:id/passwordSet a user's password and revoke their sessions. Owner only.
DELETE /admin/users/:idDelete an operator and their sessions. Owner only.
GET /admin/users/:id/sessionsThat user's live sessions. Never includes the stored token hash. Owner only.

There is no per-session revoke: disabling the account, changing its role, or setting a new password revokes every session it has.

The root operator (DASH_ROOT_USER) never appears in GET /admin/users and cannot be edited through the API: it is an environment identity, not a row. See Security → Dashboard authentication.

Extensions

RoutePurpose
GET /admin/extensionsLoaded definitions and configured instances, with live status.
GET /admin/extensions/artifactsList uploaded extension modules (by key).
GET /admin/extensions/artifacts/:key/versionsVersion history for one module key.
POST /admin/extensions/artifactsUpload a new version of a module (key, code). Triggers a hot-reload on every replica.
POST /admin/extensions/artifacts/:key/activateRoll back/forward to a specific previously-uploaded version.
DELETE /admin/extensions/artifacts/:keyDelete a module and all its versions.
GET /admin/extensions/instancesList configured instances (definition bindings).
POST /admin/extensions/instancesCreate an instance (id, definition, plus enabled/critical/priority/match/config).
PATCH /admin/extensions/instances/:idUpdate an instance.
DELETE /admin/extensions/instances/:idRemove an instance.
POST /admin/extensions/:id/resetClear a tripped circuit breaker and re-activate the instance for the current process.

Full walkthrough, hook semantics, and failure behavior: Runtime extensions.

Next steps

On this page