Skip to content

Admin Ai

Get Platform Ai Settings

Current platform AI scalars. Null fields defer to the env defaults.

Responses

StatusDescriptionBody
200Successful ResponsePlatformAISettingsResponse
422Validation ErrorHTTPValidationError

Update Platform Ai Settings

Update platform AI scalars.

Values land in platform_settings and are re-read per run, so a change here reaches running workers within their catalog/settings TTL — no deploy.

The default model is checked against the catalog before it is stored: it is what every org without a default of its own runs on, so a typo’d or retired id here would silently break all of them at the next turn instead of here, now, with a list of the ids that would have worked.

Feature models are checked the same way and against a harder deadline: an uncatalogued id there is not a broken call, it is a working one that bills nothing while the platform pays the provider, and nothing downstream would report it.

Request body (required)

FieldTypeRequiredDescription
compaction_enabledbooleanno
compaction_thresholdnumberno
default_modelPlatformDefaultModelInputno
default_reasoning_effortstringno
feature_modelsobjectno
max_reasoning_effortstringno
mode_high_effortModePresetInputno
mode_normalModePresetInputno
mode_trivialModePresetInputno
thinking_enabledbooleanno

Responses

StatusDescriptionBody
200Successful ResponsePlatformAISettingsResponse
422Validation ErrorHTTPValidationError

Get Platform Route Keys

Which key each inference route would spend, masked, with its provenance.

Note is_set and source can disagree: a stored route key that no longer decrypts reads as set and falls through to legacy/env, which is exactly the state a rotated ENCRYPTION_KEY produces.

Its own path rather than a field on GET /ai/routes — the route list is not a secret-adjacent read, and keeping them apart means a client that only renders routes never asks for key state at all.

Responses

StatusDescriptionBody
200Successful ResponsePlatformRouteKeysResponse
422Validation ErrorHTTPValidationError

Set Platform Route Key

Store or clear one route’s platform key.

Stored encrypted, in a row created on first write — which is what makes this surface open-ended: a route created in the admin panel has a working key slot immediately, with no code change, no def and no migration. Clearing falls back down the ladder (legacy provider key, then the env var) rather than to nothing.

Refused for a route that spends another route’s key: the whole point of credential_route_id is that the secret is stored once, and accepting a write here would create the second copy it exists to prevent. The error names the route to edit instead.

The cache is dropped after the commit, so a rotation reaches the API and every agent-runner immediately instead of within the cache TTL.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
api_keystringyes

Responses

StatusDescriptionBody
200Successful ResponsePlatformRouteKeysResponse
422Validation ErrorHTTPValidationError

List Inference Routes

Every route, enabled or not, in display order.

Responses

StatusDescriptionBody
200Successful ResponseInferenceRouteResponse[]
422Validation ErrorHTTPValidationError

Create Inference Route

Add a route. slug must be unique — it’s what catalog rows will store.

Request body (required)

FieldTypeRequiredDescription
base_urlstringno
credential_route_idstring (uuid)no
dialect"anthropic" | "openai" | "voyage"yes
enabledbooleanno
gateway_provider_slugstringno
labelstringyes
slugstringyes
sort_orderintegerno

Responses

StatusDescriptionBody
201Successful ResponseInferenceRouteResponse
422Validation ErrorHTTPValidationError

Get Inference Route

One route by id.

Exists so a detail view gets a real 404 for a deleted or mistyped id, rather than deriving “not found” from a successful list response — which reports the same condition as an empty filter, and cannot distinguish “this route is gone” from “the list call failed and returned nothing”.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseInferenceRouteResponse
422Validation ErrorHTTPValidationError

Update Inference Route

Update a route.

Switching enabled off is refused with 409 while anything is still pinned to it, naming what — there is no lazy retirement for routes, so a disabled route is a hard fail at dispatch rather than a redirect, and the blast radius of getting it wrong is a mode preset taking down every trivial-effort surface at once. POST /ai/routes/{id}/repoint is what makes the refusal satisfiable; GET /ai/routes/{id}/pins is the same answer without attempting the write.

Which half of the enabled asymmetry this extends. Route enabled gates dispatch through a route and deliberately not credential resolution: resolve_credential_route walks through disabled rows so that switching anthropic-direct off to push everyone onto anthropic-cf does not also strip the gateway route of the key it spends. This guard is on the dispatch side — it counts what would stop dispatching, and asks nothing about who spends whose key. The credential side already has its own, separate refusal: _ensure_usable_credential_route rejects a disabled route as somebody’s credential_route_id, and the delete path refuses while dependants exist.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
base_urlstringno
credential_route_idstring (uuid)no
dialect"anthropic" | "openai" | "voyage"no
enabledbooleanno
gateway_provider_slugstringno
labelstringno
sort_orderintegerno

Responses

StatusDescriptionBody
200Successful ResponseInferenceRouteResponse
422Validation ErrorHTTPValidationError

Delete Inference Route

Remove a route.

Refused with 409 while another route spends this one’s key. Not a cascade and not a null-out: promoting anthropic-cf to “spends its own key” because anthropic-direct was deleted would send requests authenticated with a key nobody ever configured, and the first anyone hears of it is a provider 401 on live traffic. Repoint the dependants first.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

Get Inference Route Pins

What is pinned to this route, without attempting a write.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseRoutePinsResponse
422Validation ErrorHTTPValidationError

POST /api/admin/ai/routes/{route_id}/repoint

Section titled “POST /api/admin/ai/routes/{route_id}/repoint”

Repoint Inference Route

Move everything pinned to this route onto another one, atomically.

The bulk repoint, and the only supported way a model changes route. All five stores move in one transaction — catalog rows, agents, prompt templates, mode presets, feature-model settings — because a partial repoint is worse than none: a mode preset left naming a route that is then disabled takes down titles, summaries, extraction and compaction simultaneously.

Afterwards the source route disables cleanly — unless an org has its own offering on it. Those are moved by nobody but the org: they are the record of where that tenant’s traffic goes and what it costs, so an admin tidying routes must not relocate them as a side effect. GET …/pins reports them separately and answers repoint_clears: false when they are there, which is the case where running this action first would be wasted.

Refusals, all 422 except the missing target:

  • the target does not exist (404) or is the source itself;
  • the target is disabled — repointing onto a route nothing may dispatch through just relocates the outage;
  • the target’s dialect cannot serve some row’s kind. Checked per row through _ensure_dialect_matches_kind, the same predicate create and the per-row repoint go through, because “a row lands on a route” has to have exactly one choke point — a chat model on voyage-direct would otherwise be reachable in bulk while being refused one at a time.

Parameters

NameInTypeRequiredDescription
route_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
target_route_idstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseRepointRouteResponse
422Validation ErrorHTTPValidationError

List Catalog Models

Every catalog row, enabled or not, in display order.

Responses

StatusDescriptionBody
200Successful ResponseModelCatalogEntryResponse[]
422Validation ErrorHTTPValidationError

Create Catalog Model

Add a model on a route. (key, route) must be unique.

A key may appear on two routes — that is the point of the pair — but only one of them may be enabled at a time, so the conflict below covers both the same-pair collision and the second-enabled-row one.

Request body (required)

FieldTypeRequiredDescription
allowed_effortsstring[]no
cache_write_price_per_1mnumber | stringno
cached_input_price_per_1mnumber | stringno
context_windowintegerno
cost_tierstringno
credit_multipliernumber | stringno
enabledbooleanno
fallback_credits_per_1k_tokensnumber | stringno
input_price_per_1mnumber | stringno
keystringyes
kindstringno
labelstringyes
max_output_tokensintegerno
output_price_per_1mnumber | stringno
providerstringyes
remote_model_idstringno
replacement_model_idstring (uuid)no
route_idstring (uuid)yes
sort_orderintegerno
supports_reasoningbooleanno
supports_visionbooleanno

Responses

StatusDescriptionBody
201Successful ResponseModelCatalogEntryResponse
422Validation ErrorHTTPValidationError

Update Catalog Model

Update a model. Switching enabled off takes effect platform-wide.

Runs already pinned to a disabled model are not rewritten: they resolve to replacement_model_id (or the platform default) lazily at dispatch.

Renaming key is refused with 409 while any live config still names the old one, listing what to repoint first — a rename is not a relabel, it changes the id sent on the wire and every consumer stores that id by value. Nothing cascades and no historical row is touched. To follow a vendor’s own id change, add a second row and point the old one’s replacement_model_id at it; that is what lazy retirement is for.

Parameters

NameInTypeRequiredDescription
entry_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
allowed_effortsstring[]no
cache_write_price_per_1mnumber | stringno
cached_input_price_per_1mnumber | stringno
context_windowintegerno
cost_tierstringno
credit_multipliernumber | stringno
deprecatedbooleanno
enabledbooleanno
fallback_credits_per_1k_tokensnumber | stringno
input_price_per_1mnumber | stringno
keystringno
kindstringno
labelstringno
max_output_tokensintegerno
output_price_per_1mnumber | stringno
providerstringno
remote_model_idstringno
replacement_model_idstring (uuid)no
route_idstring (uuid)no
sort_orderintegerno
supports_reasoningbooleanno
supports_visionbooleanno

Responses

StatusDescriptionBody
200Successful ResponseModelCatalogEntryResponse
422Validation ErrorHTTPValidationError

Delete Catalog Model

Remove a model from the catalog.

Prefer disabling: a deleted row loses its replacement pointer, so anything still pinned to the key falls all the way through to the platform default.

Parameters

NameInTypeRequiredDescription
entry_idpathstring (uuid)yes

Responses

StatusDescriptionBody
204Successful Response
422Validation ErrorHTTPValidationError

POST /api/admin/models/{entry_id}/sync-price

Section titled “POST /api/admin/models/{entry_id}/sync-price”

Sync Catalog Model Price

Re-price one model from the community LiteLLM dataset.

A convenience for identifying prices, not a runtime dependency: runs bill from the catalog, and this is one way to fill it. Deliberate per-row action, so it does overwrite what’s there — the boot-time sweep is the one that only fills blanks, precisely so a hand-typed price survives it.

Parameters

NameInTypeRequiredDescription
entry_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseModelCatalogEntryResponse
422Validation ErrorHTTPValidationError

Get Org Ai Settings

One org’s runtime overrides, plus the platform values behind its nulls.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Responses

StatusDescriptionBody
200Successful ResponseOrgRuntimeSettingsResponse
422Validation ErrorHTTPValidationError

PATCH /api/admin/orgs/{org_id}/ai-settings

Section titled “PATCH /api/admin/orgs/{org_id}/ai-settings”

Update Org Ai Settings

Set (or clear) one org’s runtime overrides on its behalf.

Writes the same rows the org’s own settings page writes, so support flipping a single org and that org flipping itself are the same operation — and the platform tier stays where everyone else reads it.

Parameters

NameInTypeRequiredDescription
org_idpathstring (uuid)yes

Request body (required)

FieldTypeRequiredDescription
compaction_enabledbooleanno
compaction_thresholdnumberno
thinking_enabledbooleanno

Responses

StatusDescriptionBody
200Successful ResponseOrgRuntimeSettingsResponse
422Validation ErrorHTTPValidationError