English
AI models
Routes are relative to <PUBLIC_BASE_URL>; request and response bodies are JSON unless stated otherwise. Authentication, scopes, pagination and the error format are described in the REST API guides.
GET /api/v1/llm-defaults
Read the resolved AI defaults
The model each purpose will actually use on this instance, after the administrator’s routing. null for a purpose no provider can serve. Model names only: no key, no catalogue.
Access — Member session or API key with scope connections:read.
Responses
200 — The defaults.
| Field | Type | Required |
|---|---|---|
defaults | object | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"defaults": {
"type": "object",
"properties": {
"classify": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"compose": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"extract": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"general": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"analyzer": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"assistant": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
}
},
"required": [ "classify", "compose", "extract", "general", "analyzer", "assistant" ],
"additionalProperties": false
}
},
"required": [ "defaults" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
GET /api/v1/llm-connections
List the AI connections
What a node selector needs: id, label, provider and default flag. Built without decrypting anything.
Access — Member session or API key with scope connections:read.
Responses
200 — The connections.
| Field | Type | Required |
|---|---|---|
connections | object[] | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"connections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string" },
"isDefault": { "type": "boolean" }
},
"required": [ "id", "provider", "label", "isDefault" ],
"additionalProperties": false
}
}
},
"required": [ "connections" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
GET /api/v1/llm-connections/{id}/models
List the models of an AI connection
restricted says whether the administrator closed the list (only these models) or whether it is a typing aid (known models, others still valid).
Access — Member session or API key with scope connections:read.
Parameters
| Name | In | Type | Required |
|---|---|---|---|
id | path | string | yes |
Responses
200 — The models.
| Field | Type | Required |
|---|---|---|
connectionId | string (uuid) | yes |
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
models | string[] | yes |
restricted | boolean | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"connectionId": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"models": { "type": "array", "items": { "type": "string" } },
"restricted": { "type": "boolean" }
},
"required": [ "connectionId", "provider", "models", "restricted" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.connection_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
GET /api/v1/admin/llm/connections
List every AI connection
Access — Member session or API key with scope admin:read (administrator role required).
Responses
200 — The connections.
| Field | Type | Required |
|---|---|---|
connections | object[] | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"connections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string" },
"isDefault": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"enabledModels": {
"anyOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "null" }
]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"provider",
"label",
"isDefault",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"enabledModels",
"updatedAt"
],
"additionalProperties": false
}
}
},
"required": [ "connections" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
POST /api/v1/admin/llm/connections
Add an AI connection
Adds a key next to the others for a provider (the provider PUT replaces the default one instead). The label is unique per instance. Recorded in the audit log, never with the key.
Access — Member session or API key with scope admin:write (administrator role required).
Request body (application/json)
| Field | Type | Required |
|---|---|---|
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
label | string | yes |
apiKey | string | no |
baseUrl | string (uri) | no |
withoutApiKey | boolean | no |
enabledModels | string[] | no |
makeDefault | boolean | no |
JSON Schema
json
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string", "minLength": 1, "maxLength": 80 },
"apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
"baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
"withoutApiKey": { "type": "boolean" },
"enabledModels": {
"minItems": 1,
"maxItems": 64,
"type": "array",
"items": { "type": "string", "minLength": 1, "maxLength": 128 }
},
"makeDefault": { "type": "boolean" }
},
"required": [ "provider", "label" ]
}Responses
201 — The connection state.
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
label | string | yes |
isDefault | boolean | yes |
hasApiKey | boolean | yes |
apiKeyHint | string | null | yes |
baseUrl | string | null | yes |
enabledModels | string[] | null | yes |
updatedAt | string (date-time) | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string" },
"isDefault": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"enabledModels": {
"anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "null" } ]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"provider",
"label",
"isDefault",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"enabledModels",
"updatedAt"
],
"additionalProperties": false
}400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
This operation accepts an Idempotency-Key header: replaying the same request with the same key returns the original response instead of acting twice. See Idempotency.
Error codes — request.bad_request, llm.encryption_disabled, llm.invalid_base_url, llm.api_key_required, llm.connection_label_taken. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
PUT /api/v1/admin/llm/connections/{id}
Update an AI connection
Rename, replace the key, change the base URL or the allowed models. An absent apiKey keeps the one in place.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
id | path | string | yes |
Request body (application/json)
| Field | Type | Required |
|---|---|---|
label | string | no |
apiKey | string | no |
baseUrl | string (uri) | no |
withoutApiKey | boolean | no |
enabledModels | string[] | no |
JSON Schema
json
{
"type": "object",
"properties": {
"label": { "type": "string", "minLength": 1, "maxLength": 80 },
"apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
"baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
"withoutApiKey": { "type": "boolean" },
"enabledModels": {
"minItems": 1,
"maxItems": 64,
"type": "array",
"items": { "type": "string", "minLength": 1, "maxLength": 128 }
}
}
}Responses
200 — The connection state.
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
label | string | yes |
isDefault | boolean | yes |
hasApiKey | boolean | yes |
apiKeyHint | string | null | yes |
baseUrl | string | null | yes |
enabledModels | string[] | null | yes |
updatedAt | string (date-time) | yes |
Same schema as POST /api/v1/admin/llm/connections.
400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — request.bad_request, llm.connection_not_found, llm.connection_label_taken, llm.invalid_base_url, llm.encryption_disabled. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
DELETE /api/v1/admin/llm/connections/{id}
Remove an AI connection
Refused (409) while a published workflow references it, unless force=true; refused when it is the default and others exist. 204 even when it no longer exists.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
id | path | string | yes |
force | query | string | no |
Responses
204 — Removed.
400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.connection_in_use, llm.connection_is_default. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
POST /api/v1/admin/llm/connections/{id}/default
Make an AI connection the default
The connection becomes the default of its provider; the previous default steps down. Recorded in the audit log.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
id | path | string | yes |
Responses
200 — The connection state.
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
label | string | yes |
isDefault | boolean | yes |
hasApiKey | boolean | yes |
apiKeyHint | string | null | yes |
baseUrl | string | null | yes |
enabledModels | string[] | null | yes |
updatedAt | string (date-time) | yes |
Same schema as POST /api/v1/admin/llm/connections.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.connection_not_found. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
GET /api/v1/admin/llm-providers
Describe every AI provider
The state of each provider (configured or not, catalogue, connections) and the resolved defaults, in one call.
Access — Member session or API key with scope admin:read (administrator role required).
Responses
200 — The providers.
| Field | Type | Required |
|---|---|---|
providers | object[] | yes |
defaults | object | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"providers": {
"type": "array",
"items": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"configured": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"preset": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"defaultBaseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrlPolicy": { "type": "string", "enum": [ "fixed", "preset", "required" ] },
"apiKey": { "type": "string", "enum": [ "required", "optional" ] },
"jsonMode": {
"type": "string",
"enum": [ "tool", "json_schema", "json_object", "none" ]
},
"supportsModelDiscovery": { "type": "boolean" },
"supportsTools": { "type": "boolean" },
"supportsVision": { "type": "boolean" },
"docsUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
},
"required": [
"provider",
"defaultBaseUrl",
"baseUrlPolicy",
"apiKey",
"jsonMode",
"supportsModelDiscovery",
"supportsTools",
"supportsVision",
"docsUrl"
],
"additionalProperties": false
},
"models": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"retentionCovered": { "type": "boolean" },
"supportsTemperature": { "type": "boolean" },
"enabled": { "type": "boolean" }
},
"required": [
"id",
"inputUsdPerMTok",
"outputUsdPerMTok",
"retentionCovered",
"supportsTemperature",
"enabled"
],
"additionalProperties": false
}
},
"enabledModels": {
"anyOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "null" }
]
},
"defaults": {
"type": "object",
"properties": {
"classify": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"compose": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"extract": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"general": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"analyzer": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
},
"required": [ "classify", "compose", "extract", "general", "analyzer" ],
"additionalProperties": false
},
"updatedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{ "type": "null" }
]
},
"connections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string" },
"isDefault": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"enabledModels": {
"anyOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "null" }
]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"provider",
"label",
"isDefault",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"enabledModels",
"updatedAt"
],
"additionalProperties": false
}
}
},
"required": [
"provider",
"configured",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"preset",
"models",
"enabledModels",
"defaults",
"updatedAt",
"connections"
],
"additionalProperties": false
}
},
"defaults": {
"type": "object",
"properties": {
"classify": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"compose": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"extract": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"general": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"analyzer": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"assistant": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
}
},
"required": [ "classify", "compose", "extract", "general", "analyzer", "assistant" ],
"additionalProperties": false
}
},
"required": [ "providers", "defaults" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
GET /api/v1/admin/llm-providers/{provider}
Describe an AI provider
Returned even without a stored key (configured: false), catalogue and defaults included.
Access — Member session or API key with scope admin:read (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
provider | path | string | yes |
Responses
200 — The provider state.
| Field | Type | Required |
|---|---|---|
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
configured | boolean | yes |
hasApiKey | boolean | yes |
apiKeyHint | string | null | yes |
baseUrl | string | null | yes |
preset | object | yes |
models | object[] | yes |
enabledModels | string[] | null | yes |
defaults | object | yes |
updatedAt | string (date-time) | null | yes |
connections | object[] | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"configured": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"preset": {
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"defaultBaseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrlPolicy": { "type": "string", "enum": [ "fixed", "preset", "required" ] },
"apiKey": { "type": "string", "enum": [ "required", "optional" ] },
"jsonMode": {
"type": "string",
"enum": [ "tool", "json_schema", "json_object", "none" ]
},
"supportsModelDiscovery": { "type": "boolean" },
"supportsTools": { "type": "boolean" },
"supportsVision": { "type": "boolean" },
"docsUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
},
"required": [
"provider",
"defaultBaseUrl",
"baseUrlPolicy",
"apiKey",
"jsonMode",
"supportsModelDiscovery",
"supportsTools",
"supportsVision",
"docsUrl"
],
"additionalProperties": false
},
"models": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"retentionCovered": { "type": "boolean" },
"supportsTemperature": { "type": "boolean" },
"enabled": { "type": "boolean" }
},
"required": [
"id",
"inputUsdPerMTok",
"outputUsdPerMTok",
"retentionCovered",
"supportsTemperature",
"enabled"
],
"additionalProperties": false
}
},
"enabledModels": {
"anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "null" } ]
},
"defaults": {
"type": "object",
"properties": {
"classify": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"compose": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"extract": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"general": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"analyzer": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
},
"required": [ "classify", "compose", "extract", "general", "analyzer" ],
"additionalProperties": false
},
"updatedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{ "type": "null" }
]
},
"connections": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"label": { "type": "string" },
"isDefault": { "type": "boolean" },
"hasApiKey": { "type": "boolean" },
"apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"enabledModels": {
"anyOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "null" }
]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"id",
"provider",
"label",
"isDefault",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"enabledModels",
"updatedAt"
],
"additionalProperties": false
}
}
},
"required": [
"provider",
"configured",
"hasApiKey",
"apiKeyHint",
"baseUrl",
"preset",
"models",
"enabledModels",
"defaults",
"updatedAt",
"connections"
],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.unsupported_provider. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
PUT /api/v1/admin/llm-providers/{provider}
Configure an AI provider
Sets or replaces the default connection of the provider. The key is required on first configuration and optional afterwards (absent keeps it). Recorded in the audit log, never with the key.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
provider | path | string | yes |
Request body (application/json)
| Field | Type | Required |
|---|---|---|
apiKey | string | no |
baseUrl | string (uri) | no |
withoutApiKey | boolean | no |
enabledModels | string[] | no |
JSON Schema
json
{
"type": "object",
"properties": {
"apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
"baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
"withoutApiKey": { "type": "boolean" },
"enabledModels": {
"minItems": 1,
"maxItems": 64,
"type": "array",
"items": { "type": "string", "minLength": 1, "maxLength": 128 }
}
}
}Responses
200 — The provider state.
| Field | Type | Required |
|---|---|---|
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
configured | boolean | yes |
hasApiKey | boolean | yes |
apiKeyHint | string | null | yes |
baseUrl | string | null | yes |
preset | object | yes |
models | object[] | yes |
enabledModels | string[] | null | yes |
defaults | object | yes |
updatedAt | string (date-time) | null | yes |
connections | object[] | yes |
Same schema as GET /api/v1/admin/llm-providers/{provider}.
400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.unsupported_provider, request.bad_request, llm.encryption_disabled, llm.invalid_base_url, llm.api_key_required, llm.model_not_allowed. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
DELETE /api/v1/admin/llm-providers/{provider}
Remove an AI provider key
204 even when nothing was stored. The audit line says whether something was actually removed.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
provider | path | string | yes |
Responses
204 — Removed.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.unsupported_provider. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
POST /api/v1/admin/llm-providers/{provider}/test
Test an AI provider
A real, minimal call. The verdict is in the body with a 200: a refused key is the result, not an error of this API. connectionId names the connection to probe; absent, the default one.
Access — Member session or API key with scope admin:write (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
provider | path | string | yes |
connectionId | query | string | no |
Responses
200 — The verdict.
| Field | Type | Required |
|---|---|---|
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
ok | boolean | yes |
model | string | yes |
latencyMs | integer | yes |
error | object | no |
JSON Schema
json
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"ok": { "type": "boolean" },
"model": { "type": "string" },
"latencyMs": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"error": {
"type": "object",
"properties": { "code": { "type": "string" }, "message": { "type": "string" } },
"required": [ "code", "message" ],
"additionalProperties": false
}
},
"required": [ "provider", "ok", "model", "latencyMs" ],
"additionalProperties": false
}400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.unsupported_provider, llm.provider_not_configured, llm.connection_not_found, llm.connection_provider_mismatch. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
GET /api/v1/admin/llm/providers/{provider}/models
Discover the models a key can see
Asks the provider itself, with the instance key (ten-minute cache). 200 even on failure, like the test. HTTP errors remain for an unknown provider, one that does not list its models, no stored key, and a connection of another provider.
Access — Member session or API key with scope admin:read (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
provider | path | string | yes |
connectionId | query | string | no |
Responses
200 — The discovered models.
| Field | Type | Required |
|---|---|---|
provider | "anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible" | yes |
ok | boolean | yes |
models | object[] | yes |
cached | boolean | yes |
fetchedAt | string (date-time) | null | yes |
error | object | no |
JSON Schema
json
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"ok": { "type": "boolean" },
"models": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"label": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
"contextLength": {
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 9007199254740991
},
{ "type": "null" }
]
},
"inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
"known": { "type": "boolean" }
},
"required": [
"id",
"label",
"contextLength",
"inputUsdPerMTok",
"outputUsdPerMTok",
"known"
],
"additionalProperties": false
}
},
"cached": { "type": "boolean" },
"fetchedAt": {
"anyOf": [
{
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
{ "type": "null" }
]
},
"error": {
"type": "object",
"properties": { "code": { "type": "string" }, "message": { "type": "string" } },
"required": [ "code", "message" ],
"additionalProperties": false
}
},
"required": [ "provider", "ok", "models", "cached", "fetchedAt" ],
"additionalProperties": false
}400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — llm.unsupported_provider, llm.discovery_not_supported, llm.provider_not_configured, llm.connection_provider_mismatch. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
GET /api/v1/admin/llm/defaults
Read the AI routing by purpose
One row per slot: the administrator’s choice, what the purpose will actually get, and whether the choice became inapplicable (key removed, model unticked).
Access — Member session or API key with scope admin:read (administrator role required).
Responses
200 — The routing.
| Field | Type | Required |
|---|---|---|
entries | object[] | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"entries": {
"type": "array",
"items": {
"type": "object",
"properties": {
"purpose": {
"type": "string",
"enum": [
"default",
"classify",
"compose",
"extract",
"general",
"analyzer",
"assistant"
]
},
"override": {
"anyOf": [
{
"type": "object",
"properties": {
"purpose": {
"type": "string",
"enum": [
"default",
"classify",
"compose",
"extract",
"general",
"analyzer",
"assistant"
]
},
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": {
"anyOf": [
{ "type": "string", "minLength": 1, "maxLength": 128 },
{ "type": "null" }
]
},
"temperature": {
"anyOf": [
{ "type": "number", "minimum": 0, "maximum": 2 },
{ "type": "null" }
]
},
"maxOutputTokens": {
"anyOf": [
{
"type": "integer",
"exclusiveMinimum": 0,
"maximum": 200000
},
{ "type": "null" }
]
},
"updatedAt": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
}
},
"required": [
"purpose",
"provider",
"model",
"temperature",
"maxOutputTokens",
"updatedAt"
],
"additionalProperties": false
},
{ "type": "null" }
]
},
"resolved": {
"anyOf": [
{
"type": "object",
"properties": {
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" }
},
"required": [ "provider", "model" ],
"additionalProperties": false
},
{ "type": "null" }
]
},
"overrideUnavailable": { "type": "boolean" }
},
"required": [ "purpose", "override", "resolved", "overrideUnavailable" ],
"additionalProperties": false
}
}
},
"required": [ "entries" ],
"additionalProperties": false
}401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
PUT /api/v1/admin/llm/defaults
Set the AI routing by purpose
Partial: absent slots do not move; provider: null clears a row. A choice on a provider without a key is accepted and reported as not yet applicable. Recorded in the audit log.
Access — Member session or API key with scope admin:write (administrator role required).
Request body (application/json)
| Field | Type | Required |
|---|---|---|
defaults | object[] | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"defaults": {
"minItems": 1,
"maxItems": 7,
"type": "array",
"items": {
"type": "object",
"properties": {
"purpose": {
"type": "string",
"enum": [
"default",
"classify",
"compose",
"extract",
"general",
"analyzer",
"assistant"
]
},
"provider": {
"anyOf": [
{
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
{ "type": "null" }
]
},
"model": {
"anyOf": [
{ "type": "string", "minLength": 1, "maxLength": 128 },
{ "type": "null" }
]
},
"temperature": {
"anyOf": [
{ "type": "number", "minimum": 0, "maximum": 2 },
{ "type": "null" }
]
},
"maxOutputTokens": {
"anyOf": [
{ "type": "integer", "exclusiveMinimum": 0, "maximum": 200000 },
{ "type": "null" }
]
}
},
"required": [ "purpose", "provider", "model" ]
}
}
},
"required": [ "defaults" ]
}Responses
200 — The routing, after write.
| Field | Type | Required |
|---|---|---|
entries | object[] | yes |
Same schema as GET /api/v1/admin/llm/defaults.
400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — request.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.
GET /api/v1/admin/llm-usage
Read the AI usage report
Aggregates per day, provider, model and context over [from, to). to defaults to now, from to thirty days earlier; the window cannot exceed a year. total.costUsd is null when no row had a price.
Access — Member session or API key with scope admin:read (administrator role required).
Parameters
| Name | In | Type | Required |
|---|---|---|---|
from | query | string | no |
to | query | string | no |
Responses
200 — The usage.
| Field | Type | Required |
|---|---|---|
from | string (date-time) | yes |
to | string (date-time) | yes |
rows | object[] | yes |
total | object | yes |
JSON Schema
json
{
"type": "object",
"properties": {
"from": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"to": {
"type": "string",
"format": "date-time",
"pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
},
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"day": { "type": "string" },
"provider": {
"type": "string",
"enum": [
"anthropic",
"openai",
"mistral",
"openrouter",
"ollama",
"openai_compatible"
]
},
"model": { "type": "string" },
"context": {
"type": "string",
"enum": [ "execution", "eval", "analyzer", "assistant" ]
},
"calls": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"inputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"outputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }
},
"required": [
"day",
"provider",
"model",
"context",
"calls",
"inputTokens",
"outputTokens",
"costUsd"
],
"additionalProperties": false
}
},
"total": {
"type": "object",
"properties": {
"calls": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"inputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"outputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
"costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }
},
"required": [ "calls", "inputTokens", "outputTokens", "costUsd" ],
"additionalProperties": false
}
},
"required": [ "from", "to", "rows", "total" ],
"additionalProperties": false
}400 — The request does not match its schema.
401 — No valid session or API key (auth.unauthenticated).
403 — Refused: insufficient role (auth.forbidden), missing scope (api_key.scope_missing, details.required names it) or a route closed to API keys (api_key.session_required).
429 — The API key exceeded its rate limit (api_key.rate_limited); Retry-After says when to retry.
Error codes — request.bad_request. The common codes (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) apply to every route; see Errors.