AI providers, model categories, and model versions installed in the system.
Common responses
These responses apply across endpoints in this section unless an endpoint documents an additional response.
Status
Meaning
Body
200
Request succeeded.
Endpoint response object.
201
Resource created.
Created resource object.
400
Invalid request.
Error envelope.
401
Missing or invalid bearer token.
Error envelope.
404
Resource not found or not visible to the current organization.
Error envelope.
AI providers
List AI providers
Return every AI provider configured in the system, sorted by display name ascending. The response is cursor-paginated; follow `next_cursor` while `has_more` is true. Providers are the top of the model hierarchy — a provider owns one or more model categories, which in turn own one or more model versions. Every provider response carries one derived boolean alongside the stored columns: `has_api_key` (an encrypted key is configured — the key itself is never returned). Every registered provider lists its models live from the vendor's own models API (`GET /v1/admin/ai-providers/:id/available-models`); the API key stored on the provider is what authenticates that call, except for Cerebras, whose catalog is public.
A picker-friendly view of what Cortex can route to **today** — nested provider → families with their **supported capabilities**. Only the four organization-facing capabilities are surfaced: `text`, `image`, `audio`, `embeddings` (the family's other declared modalities such as `video` or `documents` are not surfaced here even when present on the latest version). Families whose latest version has none of these supported capabilities are omitted, and providers with no surviving families are dropped from the response. Use this to power model-picker UIs.
Register a new provider. `slug` is globally unique and is the stable identifier the rest of the system uses (URL paths, logs, audit events). Display `name` can be changed freely later.
POST/v1/admin/ai-providers
Request body
Name
Type
Required
Description
name
string
Required
Display name shown in admin UIs and dashboards. 1–100 chars.
slug
string
Required
Lowercase URL-safe identifier matching `^[a-z0-9-]+$`. Must be globally unique. 1–50 chars. Used in logs and audit trails — choose carefully, renames cascade.
website
string
Optional
Optional vendor URL. Max 255 chars. Pass null to clear.
api_key
string
Optional
Optional API key used to call the provider. Write-only: stored encrypted at rest and never returned by any endpoint — responses expose only the `has_api_key` boolean. 1–512 chars.
Provider still has one or more model categories attached.
`{ error }`
403
Admin access required — caller lacks the system admin role or the credential cannot reach admin routes.
`{ error, code, request_id }`
204 No Contenttext
(empty response body)
List a provider's available models
Return the provider's LIVE model list — every id the vendor's own models API currently serves — so a category's versions can be picked from it. Each row is one vendor model id (the exact string handed to the vendor at inference), with `display_name` (the vendor's own display name, or a readable form of the id — `gpt-5.5-sol` → `GPT 5.5 Sol` — when the vendor publishes none; never null, and exactly what a version created from the row stores), `kind` (`chat`, `image`, `audio`, `embedding` or `other`, classified from the vendor's own type field where it has one and from the id otherwise), `deprecated` (the vendor has announced retirement), `owned_by`, `released_at`, `tracked` (`null`, or the ACTIVE category that already holds this id — a soft-deleted category does not count, so its ids are offered again), and `fields`: the version columns an import would write, built from what the vendor publishes (context window, output limit, pricing, capability flags, modalities) over an honest per-kind skeleton — everything the vendor does not publish is `null`/`false`, never invented — and `vendor`: the raw facts the vendor published, before they are laid over the skeleton. `listing` describes the source: `documented` is false when the vendor has no documented list endpoint and the list is maintained by hand (Z.ai), `docs_url` links the vendor reference, `fetched_at` is when the list was read. Lists are cached server-side for one minute; pass `?refresh=1` to bypass.
GET/v1/admin/ai-providers/:id/available-models
Path parameters
Name
Type
Required
Description
id
string
Required
The provider's UUID.
Query parameters
Name
Type
Required
Description
refresh
string
Optional
`1` to bypass the one-minute server cache and re-read the vendor.
This provider has no model listing integration (a provider created from the console with a slug Cortex does not integrate). Enter versions manually.
`{ error }`
409
No API key configured for this provider — add one via `PATCH /admin/ai-providers/:id` first. Cerebras lists from a public catalog and never returns this.
`{ error }`
502
The vendor's models API failed (network error, non-OK response, timeout, or an endless pagination).
`{ error }`
403
Admin access required — caller lacks the system admin role or the credential cannot reach admin routes.
Re-read the provider's live model list and bring its tracked versions up to date with what the vendor publishes. For every version whose id the vendor still lists, the vendor's facts (context window, output limit, pricing, capability flags, modalities) are written over the stored values and `last_listed_at` and `upstream_deprecated` are stamped; `updated` counts rows where something changed, `unchanged` the rest. Versions the vendor no longer lists are returned in `unlisted`, and versions the vendor flags as retiring in `deprecated` — as badges for an admin to act on. Refresh never creates a version (use `POST /admin/ai-model-families/:id/import-versions`), never deactivates one, and never touches the stored `version` string, `is_active` or `supports_streaming`. Pass `family_ids` to scope the run to specific categories. `errors` is non-empty on a 200 when the vendor returned an empty list, in which case nothing was compared.
POST/v1/admin/ai-providers/:id/sync-models
Path parameters
Name
Type
Required
Description
id
string
Required
The provider's UUID.
Request body
Name
Type
Required
Description
familyIds
array
Optional
Optional array of category UUIDs to scope the refresh to. Omit to refresh every category under this provider.
List every model category in the catalog, sorted by name ascending. Pass `?providerId=<uuid>` to scope to a single provider — useful when populating a provider-specific picker.
GET/v1/admin/ai-model-families
Query parameters
Name
Type
Required
Description
providerId
string
Optional
Filter to one provider's categories. UUID. Omit to return all categories across all providers.
Create a new model category under a provider. **Slug uniqueness is per-provider, not global** — `claude-sonnet` can exist on Anthropic and on a fictional `claude-sonnet` slug on another provider without collision. If the slug matches a **soft-deleted** (inactive) category of this provider, that category is revived instead: reactivated together with its versions, renamed to the submitted `name`/`description`, and returned with its original id. Once created, attach versions via `POST /admin/ai-model-versions`. The API resource keeps its `ai-model-families` path for compatibility.
POST/v1/admin/ai-model-families
Request body
Name
Type
Required
Description
providerId
string
Required
UUID of the parent provider. Must exist.
name
string
Required
Display name. 1–100 chars.
slug
string
Required
Lowercase URL-safe identifier matching `^[a-z0-9-]+$`. Unique **within the provider** — different providers can reuse the same slug. 1–100 chars.
description
string
Optional
Free-form description shown in admin UIs. Pass null to clear.
Add one or more vendor model ids — rows from `GET /admin/ai-providers/:id/available-models` — to a category as versions. Each version is written with exactly the `fields` the listing showed for that id (the vendor's published facts over a per-kind skeleton), plus its `display_name` as the listing showed it, `upstream_deprecated` and `last_listed_at`. Every id must be in the vendor's current list (400 listing the unknown ones) and must not already be tracked by any ACTIVE category of the same provider (409 naming the category) — a vendor id lives in at most one category per provider, because the worker resolves some versions by that string alone. Versions are created **inactive** unless `is_active` is true: several vendors publish no pricing, and an unpriced active version meters usage at zero. If the category has no `latest_version_id` yet, the newest imported version by release date (or, with no dates, the first listed) is promoted; an existing latest is never moved. The whole import is one transaction.
Return a single category by ID. `latestVersionId` is null until you create a version and explicitly promote it; promotion happens through `PATCH /admin/ai-model-families/:id` with `latestVersionId`.
Patch a category's editable fields. Reassigning to a different provider (`providerId`) is supported but rare — usually only after a vendor rebrand. Slug uniqueness is re-checked against the destination provider's categories. Setting `latestVersionId` promotes the version Cortex actually serves for this category — pass a version's UUID to promote it, or null to clear it.
PATCH/v1/admin/ai-model-families/:id
Path parameters
Name
Type
Required
Description
id
string
Required
The category's UUID.
Request body
Name
Type
Required
Description
providerId
string
Optional
Reassign to a different provider. Must exist.
name
string
Optional
New display name. 1–100 chars.
slug
string
Optional
New slug. Lowercase URL-safe. Per-provider uniqueness applies.
description
string
Optional
Free-form description. Pass null to clear.
latestVersionId
string
Optional
UUID of the version to promote as this category's served version, or null to clear it. Must belong to this category — a version from another category is rejected.
isActive
boolean
Optional
Activate or deactivate the category. Reactivating a soft-deleted category is a plain flag flip — its versions keep their own active state.
Remove a category from the catalog together with all of its versions. If any organization has selected this category in its AI-model configuration, the delete refuses unless `replacementFamilyId` names another active category — in which case those selections are reassigned to the replacement first (pinned to its latest version), all in one transaction. Categories whose versions have recorded usage events are **soft-deleted**: the category and its versions are deactivated but kept, so usage and billing history stays attributable — the response is then `200 { result: "deactivated" }` instead of 204. A soft-deleted category is revived by adding the model back: `POST /admin/ai-model-families` with the same slug. Use `GET /admin/ai-model-families/:id/usage` to inspect selections and recorded usage before deleting.
DELETE/v1/admin/ai-model-families/:id
Path parameters
Name
Type
Required
Description
id
string
Required
The category's UUID.
Request body
Name
Type
Required
Description
replacementFamilyId
string
Optional
UUID of an active category to take over this category's organization AI-model selections. Required when any selection references the category; must be a different, existing, active category.
soft
boolean
Optional
Force the soft-delete outcome even when the category has no recorded usage: the category and its versions are deactivated and kept. Reverse it later with `PATCH { isActive: true }`.
Category hard-deleted (selections reassigned first when a replacement was given). No response body.
—
200
Category was soft-deleted (recorded usage, or `soft: true` was passed): category and versions deactivated, rows kept.
`{ result: "deactivated" }`
400
`replacementFamilyId` is the category itself, or the replacement category is inactive.
`{ error }`
404
Category or replacement category not found.
`{ error }`
409
Category is referenced by organization AI-model selections and no replacement was given.
`{ error }`
403
Admin access required — caller lacks the system admin role or the credential cannot reach admin routes.
`{ error, code, request_id }`
204 No Contenttext
(empty response body)
Get model category usage
List the organization AI-model selections that reference this category — one row per organization and selection key (a capability like `text`, or an activity like `thinking`). An empty list means the category can be deleted without a replacement. `has_recorded_usage` reports whether any of the category's versions have usage events; if true, a DELETE will soft-delete (deactivate) the category rather than remove it.
Register a new version under a model. The `version` string is the vendor identifier (e.g. `claude-sonnet-4-6-20251001`) and is **unique within the model** — different models can have the same version string. Pricing fields are stored as integer **cents per 1,000,000 tokens** (so `inputCostPer1m: 300` means $3.00 per 1M tokens). Modalities accept any combination of `text`, `image`, `audio`, `video`, `embeddings`, `documents`. `display_name` is how the version is listed in the console; when omitted, null or blank it is derived from `version` (`gpt-5.5-sol` → `GPT 5.5 Sol`), so a version is never nameless. `last_listed_at` and `upstream_deprecated` are stamped by import and refresh and cannot be set here.
POST/v1/admin/ai-model-versions
Request body
Name
Type
Required
Description
modelId
string
Required
UUID of the parent model. Must exist.
version
string
Required
Vendor version identifier. Unique within the model. 1–100 chars.
displayName
string
Optional
How the version is listed in the console. Trimmed, ≤200 chars. Omitted, null or blank: derived from `version`.
inputModalities
array
Optional
Array of supported input modalities. Each entry: `text`, `image`, `audio`, `video`, `embeddings`, `documents`. Defaults to `[]`.
outputModalities
array
Optional
Array of output modalities the model can produce. Same value set as inputModalities. Defaults to `[]`.
supportsToolUse
boolean
Optional
Whether the model can call tools / functions. Default false.
supportsStreaming
boolean
Optional
Whether the model streams partial completions. Default false.
supportsStructuredOutput
boolean
Optional
Whether the model can be constrained to a JSON Schema. Default false.
supportsThinking
boolean
Optional
Whether the model exposes a chain-of-thought / extended-thinking mode. Default false.
capabilities
object
Optional
Free-form JSON for any vendor-specific capability flags that don't fit the typed fields above. Defaults to `{}`.
contextWindow
number
Optional
Maximum total tokens the model can ingest in a single call. Positive integer. Null when unbounded or unknown.
maxOutputTokens
number
Optional
Maximum tokens the model will emit in a single response. Positive integer. Null when unbounded.
inputCostPer1m
number
Optional
Input token cost in **integer cents per 1M tokens**. Non-negative. Null when pricing is not configured.
outputCostPer1m
number
Optional
Output token cost in cents per 1M tokens. Non-negative integer. Null when not configured.
cachedInputCostPer1m
number
Optional
Cached / cache-read input cost in cents per 1M tokens. Non-negative integer. Null when caching is not priced separately.
cacheCreationCostPer1m
number
Optional
Cost to write to the prompt cache, in cents per 1M tokens. Non-negative integer. Null when the vendor doesn't price cache writes separately.
audioCostPer1kMinutes
number
Optional
Audio input cost in cents per 1,000 minutes. Non-negative integer. Null when the model doesn't accept audio or isn't priced for it.
isActive
boolean
Optional
Whether the version is selectable by organizations. Default true. Setting false hides it from the catalog endpoint without deleting it.
releasedAt
string
Optional
ISO-8601 datetime the vendor publicly released this version. Pass null when unknown.
A version with that id already exists in this category, or another ACTIVE category of the same provider already tracks it (the error names that category).
`{ error }`
403
Admin access required — caller lacks the system admin role or the credential cannot reach admin routes.
Patch a version's fields. Reassigning to a different model is supported (`model_id`); the version-string uniqueness check is re-run against the destination model. Setting `is_active: false` hides the version from the catalog endpoint without deleting it — useful for sunsetting an older model while preserving usage history.
PATCH/v1/admin/ai-model-versions/:id
Path parameters
Name
Type
Required
Description
id
string
Required
The version's UUID.
Request body
Name
Type
Required
Description
modelId
string
Optional
Reassign to a different model. Must exist.
version
string
Optional
New version string. Unique within the (destination) category.
displayName
string
Optional
New console name. Trimmed, 1–200 chars; blank or null is rejected — a version always has a name.
inputModalities
array
Optional
Replacement input modalities array.
outputModalities
array
Optional
Replacement output modalities array.
supportsToolUse
boolean
Optional
Tool-use capability flag.
supportsStreaming
boolean
Optional
Streaming capability flag.
supportsStructuredOutput
boolean
Optional
Structured-output capability flag.
supportsThinking
boolean
Optional
Extended-thinking capability flag.
capabilities
object
Optional
Replacement free-form capabilities object.
contextWindow
number
Optional
Maximum input tokens. Pass null to clear.
maxOutputTokens
number
Optional
Maximum output tokens. Pass null to clear.
inputCostPer1m
number
Optional
Input cost in cents per 1M tokens. Pass null to clear.
outputCostPer1m
number
Optional
Output cost in cents per 1M tokens. Pass null to clear.
cachedInputCostPer1m
number
Optional
Cached-input cost in cents per 1M tokens. Pass null to clear.
cacheCreationCostPer1m
number
Optional
Cost to write to the prompt cache, in cents per 1M tokens. Pass null to clear.
audioCostPer1kMinutes
number
Optional
Audio input cost in cents per 1,000 minutes. Pass null to clear.
isActive
boolean
Optional
Toggle availability. Setting false sunsets the version.
Invalid body (for example a blank or null `display_name`), or the target category (model_id) belongs to a different provider than the version's current category.
`{ error }`
404
Version or destination category not found.
`{ error }`
409
The new version string collides with another version in the same category, or another ACTIVE category of the same provider already tracks it (the error names that category).
`{ error }`
403
Admin access required — caller lacks the system admin role or the credential cannot reach admin routes.
Permanently remove a version from the catalog. Three guards apply: (1) refuses if any category points at this version as its `latest_version_id` — promote a different version on that category first; (2) refuses if any organization has pinned this version in its AI-model selections — point them at another version first; (3) refuses if any usage event references this version — usage history blocks deletion to preserve audit integrity. Sunsetting via `PATCH { is_active: false }` is usually a better choice for retired-but-historical versions.