Skip to Content
API ReferenceEndpointsPlatform Settings

Platform Settings Endpoints

Tenant-level LLM runtime configuration — default/fallback model, per-execution budgets, and daily token/cost caps — managed from the admin Platform Settings page. Served by the agent-config-service (port 8010) and proxied through the API gateway at http://localhost:8000/api/v1/settings*.

Endpoints

MethodPathDescription
GET/settingsTenant settings + effective values + sources (any authenticated identity)
PUT/settingsUpsert tenant settings (admin)
GET/settings/model-optionsCurated model picker
GET/settings/user-overridesList per-user daily budget overrides (admin)
PUT/settings/user-overrides/{user_id}Upsert a per-user override (admin)
DELETE/settings/user-overrides/{user_id}Remove a per-user override (admin)

All six settings fields are nullable — null means inherit the orchestration engine’s env default (DEFAULT_LLM_MODEL, DEFAULT_MAX_TOKENS_PER_EXECUTION, DEFAULT_MAX_COST_PER_EXECUTION).


GET /settings

Return the tenant’s stored settings, the resolved effective values, and a sources map showing whether each effective value came from the tenant row (tenant) or the env default (default). Any authenticated identity may call this — the orchestration engine fetches it at every turn start with X-Tenant-Id/X-User-Id headers. When the calling user has a budget override, it is returned as user_override in the same fetch.

curl http://localhost:8000/api/v1/settings \ -H "Authorization: Bearer $TOKEN"

Response (200):

{ "tenant_id": "00000000-0000-0000-0000-000000000001", "settings": { "default_model": "anthropic/claude-sonnet-5", "fallback_model": "anthropic/claude-haiku-4-5", "max_tokens_per_execution": null, "max_cost_usd_per_execution": null, "max_tokens_per_day": 2000000, "max_cost_usd_per_day": 50.0, "updated_by": "9b2f1c4e-...", "updated_at": "2026-07-04T10:00:00Z" }, "effective": { "default_model": "anthropic/claude-sonnet-5", "fallback_model": "anthropic/claude-haiku-4-5", "max_tokens_per_execution": 100000, "max_cost_usd_per_execution": 5.0, "max_tokens_per_day": 2000000, "max_cost_usd_per_day": 50.0 }, "sources": { "default_model": "tenant", "fallback_model": "tenant", "max_tokens_per_execution": "default", "max_cost_usd_per_execution": "default", "max_tokens_per_day": "tenant", "max_cost_usd_per_day": "tenant" }, "user_override": null }

Reads are Redis-cached at settings:{tenant_id} (TTL 300s; deleted on every write).


PUT /settings

Upsert the tenant settings. Admin-only. Every field is optional; sending null clears a field back to inherit. Model fields are validated against the curated list from /settings/model-options — free-text model ids are rejected.

curl -X PUT http://localhost:8000/api/v1/settings \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{ "default_model": "anthropic/claude-sonnet-5", "fallback_model": "anthropic/claude-haiku-4-5", "max_cost_usd_per_day": 50.0 }'

Response (200): the same shape as GET /settings, reflecting the new values.

Every write lands an HMAC-signed audit row (action platform_settings.update) and invalidates the Redis cache.

The fallback_model, when set, gives every orchestration LLM call site one retry on that model when the primary model errors.


GET /settings/model-options

The curated model picker backing the settings UI. Ids are LiteLLM routing ids.

curl http://localhost:8000/api/v1/settings/model-options \ -H "Authorization: Bearer $TOKEN"

Response (200):

[ {"id": "anthropic/claude-sonnet-5", "label": "Claude Sonnet 5", "provider": "anthropic", "context_window": "1M", "notes": "Best speed/intelligence balance; current box default."}, {"id": "anthropic/claude-opus-4-8", "label": "Claude Opus 4.8", "provider": "anthropic", "context_window": "1M", "notes": "Most capable; higher cost per token."}, {"id": "anthropic/claude-sonnet-4-6", "label": "Claude Sonnet 4.6", "provider": "anthropic", "context_window": "1M", "notes": "Previous-generation Sonnet."}, {"id": "anthropic/claude-haiku-4-5", "label": "Claude Haiku 4.5", "provider": "anthropic", "context_window": "200K", "notes": "Fastest and cheapest; simple tasks."} ]

GET /settings/user-overrides

List the tenant’s per-user daily budget overrides. Admin-only. Per-user overrides cap individual users; the tenant daily caps remain the aggregate ceiling.

curl http://localhost:8000/api/v1/settings/user-overrides \ -H "Authorization: Bearer $TOKEN"

Response (200):

[ { "user_id": "3c8a97d2-...", "max_tokens_per_day": 250000, "max_cost_usd_per_day": 10.0, "updated_by": "9b2f1c4e-...", "updated_at": "2026-07-04T10:00:00Z" } ]

The user picker for this table is fed by the auth service’s GET /auth/users directory.


PUT /settings/user-overrides/{user_id}

Upsert a per-user override. Admin-only. At least one of max_tokens_per_day / max_cost_usd_per_day must be set. Audit action: platform_settings.user_override.upsert.

curl -X PUT http://localhost:8000/api/v1/settings/user-overrides/$USER_ID \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"max_tokens_per_day": 250000, "max_cost_usd_per_day": 10.0}'

Response (200): the stored override.


DELETE /settings/user-overrides/{user_id}

Remove a per-user override — the user falls back to the tenant daily caps. Admin-only. Audit action: platform_settings.user_override.delete.

curl -X DELETE http://localhost:8000/api/v1/settings/user-overrides/$USER_ID \ -H "Authorization: Bearer $TOKEN"

Response (204): no content.


How the settings are enforced

At every turn start (both POST /execute and the SSE stream) the orchestration engine fetches GET /settings and resolves request override → tenant setting → env default (a fetch failure degrades to the env defaults). Daily caps are checked against Redis usage counters before a turn starts — 429 on /execute, a terminal SSE error event on the stream — and incremented with actual usage after each turn, so an in-flight turn can overshoot; the next turn is blocked. See Orchestration Engine.

Per-turn thinking override (R40.5)

thinking_mode accepts a bounded request override: POST /execute takes a thinking_mode body field and the SSE stream endpoint takes a thinking_mode query parameter ("adaptive" or "disabled"). A request may select a mode at or below the tenant’s effective thinking_mode (ordering: disabled < adaptive); a request above the ceiling — or an unknown value — is clamped to the ceiling and logged, never rejected. This preserves the cost-protection intent of the tenant setting: users can turn thinking down per turn, never up past what the tenant allows.

Last updated on