Management API
Every route of the Management API under /api/v1, with its query parameters and request-body fields.
28 min readUpdated for v2.4.0Edit on GitHub
Conventions
The gateway serves the Management API on the admin listener
(OBLETH_ADMIN_LISTEN, default 0.0.0.0:9180). The dashboard uses the same
routes. Keep the port on a private network; see
Security.
| Topic | Rule |
|---|---|
| Auth | Authorization: Bearer <OBLETH_ADMIN_TOKEN> on every route except the three public ones below; compared in constant time; wrong or missing is 401 |
| Body | JSON, Content-Type: application/json |
| Audit actor | Every write records an audit row; the actor is the X-Obleth-Audit-Actor header, default admin |
| Errors | {"error": "<message>"} with 400 invalid input or refused URL, 401, 403 protected target (the reserved control-plane tenant), 404 unknown id (also a nested id under the wrong parent), 409 name conflict, 502 saved in Postgres but the Redis cache update failed (run POST /api/v1/resync) |
| Secrets | Write-only. Responses carry api_key_set, auth_header_set, upstream_header_names, password_set, slack_webhook_set, jwt_set instead. Omit a secret on update to keep it |
| Partial updates | Settings bodies: an omitted field keeps its stored value. Resource PUTs note their own rules below |
| Pagination | List routes return everything. Request logs page with before_ms + before_request_id; the audit log with before_id |
| Time | Usage reads take Unix milliseconds (since_ms, until_ms); the audit log and budgets use RFC 3339 |
Public and system routes:
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/health | Liveness; plain-text ok. No auth |
GET | /api/v1/version | version, git_sha, built_at (null for local builds). No auth |
GET | /api/v1/openapi.json | Generated OpenAPI document. No auth |
POST | /api/v1/resync | Rebuild the Redis resolver cache (keys, models, aliases, MCP servers) from Postgres and prune stale entries |
GET | /api/v1/system/control-plane-key | The dashboard's reserved data-plane secret, for its server side only |
POST /api/v1/resync returns counts: keys, keys_pruned, models,
model_names_pruned, mcp_servers, mcp_servers_pruned. It is the retry for
any 502, and Settings › Data and backups › Reconcile cache runs it. See
Redis layout.
curl -X POST http://localhost:9180/api/v1/resync -H "Authorization: Bearer $TOKEN"
Tenants and keys
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/tenants | Create a tenant |
GET | /api/v1/tenants | List tenants |
GET | /api/v1/tenants/{id} | Get a tenant |
PUT | /api/v1/tenants/{id} | Update its profile |
DELETE | /api/v1/tenants/{id} | Delete a tenant and its keys |
PATCH | /api/v1/tenants/{id}/status | Set status |
PATCH | /api/v1/tenants/{id}/schedule | Set access hours and dates |
PATCH | /api/v1/tenants/{id}/budget | Set the tenant budget |
PATCH | /api/v1/tenants/{id}/allowlist | Set the models it may use |
PATCH | /api/v1/tenants/{id}/guardrails | Set or clear its guardrails policy |
PATCH | /api/v1/tenants/{id}/compression | Set or clear its compression policy |
PATCH | /api/v1/tenants/{id}/weight | Set its fairshare weight |
PATCH | /api/v1/tenants/{id}/group | Move it to another fairshare group |
PUT | /api/v1/tenants/{id}/quota | Set token rate and in-flight cap |
PUT | /api/v1/tenants/{id}/tracing | Span tracing on or off for all its keys |
PUT | /api/v1/tenants/{id}/synthetic | Mark it as benchmark or test traffic |
POST | /api/v1/tenants/{id}/keys | Create a key; the secret is returned once |
GET | /api/v1/keys | List keys, never secrets; ?tenant_id= for one tenant |
PUT | /api/v1/keys/{id} | Replace a key's settings |
DELETE | /api/v1/keys/{id} | Delete a key |
PUT | /api/v1/keys/{id}/disabled | Turn a key off or on |
PUT | /api/v1/keys/{id}/tenant | Move a key to another tenant |
PUT | /api/v1/keys/{id}/tracing | Span tracing on or off for one key |
PUT | /api/v1/keys/{id}/end-user-fairshare | Per-user fairshare on or off for one key; see One key, many people |
GET | /api/v1/keys/{id}/usage | One key's activity summary |
GET | /api/v1/budgets/usage | Every capped tenant and key budget with its use this period |
Request bodies. Task pages: Tenants and keys, Quotas and rate limits, Guardrails.
| Route | Field | Notes |
|---|---|---|
POST /tenants | name | Required |
POST /tenants | weight, tokens_per_minute, max_in_flight, fairshare_group, synthetic | Optional |
PUT /tenants/{id} | name, description, organization, contact_email | name required |
…/status | status | active, suspended, archived; the last two refuse traffic with 403 |
…/schedule | timezone | IANA zone, default UTC |
…/schedule | active_from, active_until | RFC 3339 or null |
…/schedule | weekly_windows | [{day, start_min, end_min}]; day 0–6 from Sunday, minutes from local midnight |
…/budget | budget_tokens, budget_cost_usd | Caps; null clears |
…/budget | budget_period | lifetime (default), monthly, term |
…/budget | budget_started_at | Defaults to now when a cap is set |
…/allowlist | allowed_models | Model names; empty permits all |
…/guardrails | policy | {action, input_scanners, output_scanners, guard_model, ban_keywords, fail_open} or null |
…/compression | policy | {enabled, code_compaction, dedup, compact_logs, allow_lossy}, or null for the global defaults |
…/weight | weight | Integer |
…/group | fairshare_group | Group name |
…/quota | tokens_per_minute, max_in_flight | tokens_per_minute required |
…/tracing (tenant) | tracing_enabled | Boolean |
…/synthetic | synthetic | Boolean; its traffic is recorded as benchmark |
PUT /keys/{id}/disabled | disabled | Boolean |
PUT /keys/{id}/tenant | tenant_id | Target tenant; secret, settings and budget move with the key, past usage stays |
PUT /keys/{id}/tracing | tracing_enabled | Boolean |
Guardrails action is block, redact or log_only; input scanners are
prompt_injection, pii, ban_keywords, harm; output scanners pii,
ban_keywords, harm; fail_open defaults to true.
API keys
POST /api/v1/tenants/{id}/keys and PUT /api/v1/keys/{id} take the same
body. PUT replaces every field, so send the current weight and
max_in_flight to keep them.
| Field | Default | Notes |
|---|---|---|
name | required | Display name |
description | "" | |
budget_tokens, budget_cost_usd | null | Key caps; null is uncapped |
budget_period | null (lifetime) | lifetime, monthly, term |
budget_started_at | now, when a cap is set | Start of the current term |
weight | 100 | Share among the tenant's keys, minimum 1 |
max_in_flight | null | The key's cap within each model pool, minimum 1 |
Create returns {"key": {…}, "secret": "sk_…"}. The secret is sk_ plus 48
hex characters; only its SHA-256 hash and an 18-character prefix are stored.
Keys with kind: "identity" are created by the gateway for a verified
identity token (Identity tokens) and managed
through the same routes.
GET /api/v1/budgets/usage returns one row per capped budget: scope
(tenant or key), id, tenant_id, period, budget_tokens,
budget_cost_usd, used_tokens, used_cost_usd, period_start, and
resets_at (monthly only).
Per-key usage summary
GET /api/v1/keys/{id}/usage returns last_used_ms, last_model,
last_status_code, and requests, token, cost, energy and CO2 totals over
the window. GET /api/v1/usage/keys/summary returns the same row for every key
with traffic, busiest first; idle keys are absent.
| Query param | Default | Notes |
|---|---|---|
since_ms | 24 hours ago | Window for the totals. On the single-key route last_used_ms ignores it |
tenant_id | unset | Bulk route only |
limit | 1000 | Bulk route only; at most 10000 |
include_internal | false | Include health probes and benchmark traffic |
A key with no traffic returns zeros and last_used_ms: 0.
Usage and request logs
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/usage | Totals per tenant |
GET | /api/v1/usage/keys | Totals per key |
GET | /api/v1/usage/keys/summary | Per-key activity summary in bulk |
GET | /api/v1/usage/models | Totals and latency per model |
GET | /api/v1/usage/speed | Output speed per model, for the window or per hour or day |
GET | /api/v1/usage/series | Time series of all traffic |
GET | /api/v1/usage/series/tenants | Time series per tenant |
GET | /api/v1/usage/series/models | Throughput and latency series for one model |
GET | /api/v1/usage/breakdown | Tenants and keys driving one model (required) |
GET | /api/v1/usage/cache | Response-cache hits and misses |
GET | /api/v1/usage/logs | Individual requests, newest first |
GET | /api/v1/usage/logs/histogram | Requests and failures per bucket, with the log's filters |
GET | /api/v1/usage/logs/facets | Busiest values per dimension, with the log's filters |
GET | /api/v1/usage/logs/{request_id}/spans | Spans recorded for one request |
GET | /api/v1/usage/daily | Permanent daily rollup over a date range |
POST | /api/v1/usage/compact | Drop raw usage older than the retention window now |
GET | /api/v1/costs | Spend per model |
GET | /api/v1/overview/summary | Configuration counts and windowed usage totals |
GET | /api/v1/stats | Live in-flight and queued counts of the answering replica |
GET | /api/v1/stats/daily | Daily MCP-call or knowledge-search counters |
Usage reads exclude internal traffic (health probes and synthetic tenants'
benchmark traffic) unless you pass include_internal=true.
| Query param | Routes | Default | Notes |
|---|---|---|---|
since_ms | all reads | 24 hours ago | Lower bound, Unix ms |
tenant_id, key_id, model | /usage, /usage/keys, /usage/models | unset | Filters |
tenant_id, model | /usage/cache | unset | Filters |
group_by | /usage | tenant | Only tenant returns rows; use /usage/keys or /usage/models for the others |
limit | /usage/keys | unset | Busiest first, at most 10000 |
limit | /usage/breakdown | 100 | Busiest first, at most 1000 |
bucket_ms | /usage/series… | 300000 | Minimum 60 s on /series, 10 s on the others; widened to at most 5000 buckets |
tenant_id | /usage/series | unset | One tenant |
model | /usage/series/models, /usage/breakdown | — | The model |
kind, days | /stats/daily | —, 7 | kind is mcp or knowledge; days 1–40 |
until_ms | /usage/speed | now | Upper bound, Unix ms (exclusive) |
bucket | /usage/speed | none | none for one row per model over the window, hour or day for one per model per hour or day; hour over more than 31 days returns days |
tz | /usage/speed | UTC | IANA time zone the hours and days are cut in |
tenant_id, key_id, model | /usage/speed | unset | Filters |
/usage/speed rows carry bucket_ms (0 for a whole-window row), model,
requests (every request, failed and cached included), rated (the requests
a speed was measured on: successful, uncached and producing tokens),
gen_tokens_per_sec (median, the same decode rate as /usage/models),
slow_tokens_per_sec (the slowest tenth), prompt_tokens_per_sec and
p50_ttft_ms.
Request logs
/usage/logs, /usage/logs/histogram and /usage/logs/facets share these
filters. Task page: Observability.
| Query param | Default | Notes |
|---|---|---|
since_ms, until_ms | 24 hours ago, open | Inclusive bounds, Unix ms |
tenant_id, key_id, model | unset | Exact match |
request_type | unset | For example chat, messages, embedding; see ClickHouse usage |
session_id, device_id | unset | Exact match |
parent_request_id | unset | The helper rows of one request |
status | all | success (2xx–3xx) or error (400 and up) |
status_code | unset | One exact status, such as 502 |
request_id | unset | Case-insensitive prefix |
include_internal | false | Include internal traffic |
traced_only | false | Log only: requests with spans |
limit | 50 | Log only: 1–200 |
before_ms, before_request_id | unset | Log only: cursor from the last row's ts_ms and request_id |
bucket_ms | 60000 | Histogram only: at least 1 s, widened to at most 400 buckets |
Spans come back ordered by start_ms, kept for 14 days. request_id is
obleth's own id, also sent to clients as x-obleth-request-id.
Daily usage rollup
GET /api/v1/usage/daily reads the permanent rollup, so it covers dates the
raw ledger has dropped. It excludes benchmark traffic.
| Query param | Default | Notes |
|---|---|---|
start_day, end_day | 7 days ago, today | Inclusive, YYYY-MM-DD |
tenant_id, model | unset | Filters |
key_id | unset | One id or a comma-separated list |
group_by | day | day, tenant, key, model, key_model, day_tenant, day_model, day_key_model |
Response fields: usage reads and logs
Overview and stats. /overview/summary returns requests, tokens, input_tokens,
output_tokens, errors, cost, has_pricing, p50_ttft_ms,
avg_ttft_ms, energy and CO2 totals, and tenant_count, active_tenants,
model_count, enabled_models, key_count. /stats returns in_flight,
queued, max_in_flight, replicas, mode and cluster_in_flight (see
Fairshare and capacity). /usage/compact returns
retention_days and partitions_dropped. /stats/daily returns days and
per-item daily counts: direct, tool_calls, errors, ms per MCP server,
or searches, hits, chunks per collection.
Request logs. Each log row carries the usage columns (request_id, ts_ms, tenant, key,
model, request_type, session_id, session_id_source, device_id,
admission, status_code, tokens, cached_input_tokens, queue_wait_ms,
ttft_ms, total_ms, cache_status, cost, energy, parent_request_id,
model_variant) plus tenant_name, key_name, key_prefix, and
has_trace. The histogram returns bucket_ms and non-empty buckets of bucket_ms,
requests, errors. Facets return up to 12 values each for status_codes,
models, tenants, keys and request_types (value, label,
requests, errors), plus failures: the most common status code and model
pairs.
Daily rollup. Rows carry day, tenant_id, key_id, model (empty or the zero UUID
outside the grouping), request, success and error counts, tokens,
estimated_tokens, cache hits and misses, avg_ttft_ms and avg_total_ms
(successful requests only), and cost, energy and CO2 as frozen at completion.
Models
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/models | Register a model |
GET | /api/v1/models | List models |
GET | /api/v1/models/{id} | Get a model |
PUT | /api/v1/models/{id} | Update a model |
DELETE | /api/v1/models/{id} | Delete a model |
GET | /api/v1/models/export | Export the registry as an obleth-models manifest |
POST | /api/v1/models/import | Merge a manifest; ?dry_run=true previews |
POST | /api/v1/models/validate | Advisory pre-flight for an upstream |
PUT | /api/v1/models/{id}/status | Set the lifecycle: staged, active, deprecated or retired |
PUT | /api/v1/models/{id}/reasoning | Set how the model takes reasoning effort: passthrough, mapped or unsupported |
PUT | /api/v1/models/{id}/listing | Set listed: whether /v1/models and /model/info offer the model |
GET / POST | /api/v1/model-families | List or create model families |
PUT / DELETE | /api/v1/model-families/{id} | Update or delete a model family |
PUT | /api/v1/models/{id}/weight | Set admission_weight |
PUT | /api/v1/models/{id}/rejected-request-fields | Set the request fields the model refuses with a 400 |
PUT | /api/v1/models/{id}/session-affinity | Set the model's own session affinity: enabled, hold_ms, max_held_percent, max_bypass_secs, each optional; {} follows the gateway |
PUT | /api/v1/models/{id}/capacity | Set max_in_flight (null for the default) |
PUT | /api/v1/models/{id}/capacity-mode | Set capacity_mode and the discovered-mode fields |
POST | /api/v1/models/{id}/autotune | Run the capacity ramp probe; recommends only |
POST | /api/v1/models/{id}/autotune/apply | Apply max_in_flight and mark the model tuned |
PUT | /api/v1/models/{id}/cache | Set cache_enabled, cache_ttl_secs |
PUT | /api/v1/models/{id}/reliability | Set timeout, retries, endpoint selection, diagnostics |
GET | /api/v1/models/{id}/knowledge | Attached knowledge collections |
PUT | /api/v1/models/{id}/knowledge | Replace attached collections: collection_ids |
GET | /api/v1/models/{id}/endpoints | List endpoints |
POST | /api/v1/models/{id}/endpoints | Add an endpoint |
PUT | /api/v1/models/{id}/endpoints/{endpoint_id} | Update an endpoint |
DELETE | /api/v1/models/{id}/endpoints/{endpoint_id} | Remove an endpoint |
GET | /api/v1/models/health | Latest health summary for every model |
POST | /api/v1/models/health | Check every eligible model now |
GET | /api/v1/models/{id}/health | One model's summary and recent checks |
POST | /api/v1/models/{id}/health/check | Check one model now |
POST | /api/v1/models/{id}/health/activate | Probe a switched-off model; turn it on if healthy |
PUT | /api/v1/models/{id}/health/config | Set one model's health-check settings |
Model fields, for POST and PUT /api/v1/models/{id} and the manifest. Task
page: Model registry.
| Field | Default | Notes |
|---|---|---|
model_name | required on create | Client-facing name; not on PUT |
upstream_model, api_base | required | api_base passes the outbound URL policy |
api_key | none | Write-only; responses show api_key_set. On PUT omit to keep, "" to clear |
upstream_headers | none | Name-to-value object added to every upstream call. Write-only; responses show upstream_header_names. On PUT omit to keep, {} removes all, a null value keeps that header |
aliases | [] | At most 16 extra names, unused elsewhere, not auto; PUT replaces the list |
variants | [] | At most 8 {name, description, boons}: names unused elsewhere that add boons. PUT replaces the list. See Variants |
description | "" | |
model_type | chat | chat, embedding, audio_transcription, audio_speech, image, video, search (a search tool: api_base is a SearXNG root) |
quantization | unknown | none, fp16, bf16, fp8, nvfp4, mxfp4, int8, int4, awq, gptq, gguf; descriptive only |
input_cost_per_token, output_cost_per_token | 0 | USD per token |
cost_per_image, cost_per_audio_second, cost_per_character | 0 | image, audio_transcription, audio_speech prices |
cost_per_video | 0 | Flat USD per created video job |
context_window | Tokens | |
admission_weight | 100 | Multiplier on tenant weight inside this pool |
max_in_flight | null | Pool size; null uses OBLETH_DEFAULT_MODEL_MAX_IN_FLIGHT. The fallback in discovered mode |
capacity_mode | static | static, tuned, discovered |
capacity_source | endpoints | discovered mode: endpoints or kubernetes |
per_replica_max_in_flight | null | Requests one backend replica takes, 1–100000; needed for kubernetes, and for endpoints unless every endpoint sets its own. PUT null clears |
capacity_headroom | 1.0 | Multiplier on the derived size, above 0 and at most 10 |
capacity_service, capacity_namespace | null | kubernetes source; unset uses OBLETH_CAPACITY_DEFAULT_SERVICE and the allowed namespaces. PUT null or "" clears |
supports_function_calling, supports_tool_choice, supports_response_schema, supports_vision | false | Capabilities |
supports_system_messages | true | |
tags | [] | auto routing tags, optional level (coding:3) |
boons | [] | vision, structured_output, compression, knowledge, image_generation, speculation, web_search |
tool_servers | [] | MCP server names for the tool loop; needs function calling |
energy_slots_per_node | 0 | Sequences that saturate one node, for per-node energy; 0 opts out |
route_bias | 1.0 | Multiplier on the auto score, clamped 0.1–3.0 |
auto_eligible | true | false keeps the model out of auto |
draft_model, verify_api_base, verify_upstream_model | "" | Speculation overrides; "" clears on PUT |
enabled | true | false on create registers it switched off; a disabled model returns 403 |
status | active | Create only: staged registers it staged; change it later with …/status |
PUT leaves omitted optional fields unchanged. Responses add id,
capacity_tuned_at, cache and reliability fields, lifecycle,
effective_status (the status in force now) and timestamps. Deleting a
model or dropping an alias evicts it from Redis. Rename a model by creating it
under the new name with the old one as an alias.
The manifest ("format": "obleth-models", "version": 1) is keyed by
model_name, never exports secrets (has_api_key, upstream_header_names),
and every other field is optional. Import creates or updates listed models,
deletes nothing, reports all errors together with 400, and returns
created, updated, unchanged and per-model action, changed_fields and
warnings. See Import models.
Endpoint fields (POST/PUT …/endpoints):
| Field | Default | Notes |
|---|---|---|
name | required | Unique within the model |
api_base | required | Passes the outbound URL policy |
api_key | the model's key | Write-only (api_key_set); PUT omit keeps, "" clears |
priority | 100 | Lower is tried first in failover |
weight | 100 | Share in load_balance |
enabled | true | |
max_in_flight | null | Per-endpoint concurrency for discovered on endpoints; PUT omit keeps, null clears |
Endpoint responses add health_status, consecutive_failures, alert_state,
and the last check's time, latency, HTTP status and message.
Model capacity and auto-tune
PUT …/capacity-mode takes capacity_mode plus the optional
capacity_source, capacity_namespace, capacity_service,
per_replica_max_in_flight and capacity_headroom, with the same rules as
PUT /api/v1/models/{id}. A discovered write the gateway cannot discover is
refused with 400. See Pool sizes.
POST …/autotune probes the upstream directly (bypassing admission) for
chat and embedding models, within 60 s and 20000 requests, and writes
nothing. See Capacity auto-tune.
| Field | Default | Notes |
|---|---|---|
workload | chat | chat or coding prompt shape |
latency_headroom | 4.0 | Multiple of single-request p99 that marks the knee; 1.5–20 |
replicas | unset | Sizes the ramp ceiling at 32 per replica |
max_concurrency | 64 | Explicit ceiling, overrides replicas; at most 512 |
The report has recommended_max_in_flight, knee_reason
(latency_degraded, plateau, max_concurrency, no_data),
baseline_p99_ms, latency_ceiling_ms, recommended_throughput_rps, and
per-step results. …/autotune/apply takes {"max_in_flight": N}.
Other per-model bodies:
| Route | Fields |
|---|---|
…/weight | admission_weight |
…/capacity | max_in_flight |
…/cache | cache_enabled, cache_ttl_secs (default 300; 0 disables). See Response cache |
…/reliability | request_timeout_secs (null uses OBLETH_UPSTREAM_TIMEOUT_SECS), max_retries (0), retry_backoff_ms (200), endpoint_selection_mode (failover, load_balance, session_hash), debug_diagnostics (false). See Reliability |
…/health/config | checks_enabled, alerts_enabled, check_interval_secs, failure_threshold, maintenance_until, maintenance_note. See Model health |
/models/validate | api_base, upstream_model, optional api_key, model_type; always 200 with reachable, wildcard, listed, warnings |
…/knowledge | collection_ids; an unknown id is 400 and changes nothing |
…/status | status (staged, active, deprecated, retired), replacement, retire_at (RFC 3339), note (at most 500 characters), redirect. Sets the whole lifecycle; see Model lifecycle |
POST /api/v1/models/health returns checked and skipped.
…/health/activate returns enabled, activated and the health detail; an
already-enabled model gets a manual check.
Fairshare and capacity
| Method | Path | What it does |
|---|---|---|
GET | /api/v1/fairshare/live | Scheduler snapshot: every live gateway in shared mode (scope: cluster), else the answering one |
GET | /api/v1/fairshare/history | Sampled scheduler history |
POST | /api/v1/fairshare/groups | Create a group: name, weight |
GET | /api/v1/fairshare/groups | List groups |
PATCH | /api/v1/fairshare/groups/{name}/weight | Set a group's weight |
GET | /api/v1/capacity | The global ceiling, max_in_flight |
PUT | /api/v1/capacity | Change the global ceiling on this replica until restart |
GET | /api/v1/capacity/discovery | Discovery settings and every discovered model's state |
GET | /api/v1/capacity/services | Services the kubernetes source can see |
Task pages: Fairshare engine, Pool sizes.
mode, in /fairshare/live, /stats and /capacity/discovery, is how the
answering replica enforces limits:
mode | Meaning | Limits reported |
|---|---|---|
shared | Cluster-wide slots in Redis, more than one replica live | Configured values |
split | Shared slots off; each replica enforces ceil(configured / replicas) | The replica's share |
fallback | Shared slots on but Redis unavailable; the split applies | The replica's share |
local | One live replica, or both settings off | Configured values |
cluster_in_flight is the count across every replica in shared mode and
null otherwise; every other count is the answering replica's own. See
Multiple gateway replicas.
/fairshare/history takes since_ms (default: start of retention) and
model (absent sums all pools). It returns interval_ms (2000),
retention_ms (OBLETH_FAIRSHARE_HISTORY_SECS), oldest_ts_ms, and points
of ts_ms, in_flight, queued, groups. /capacity/services takes
?model= (name or id) to add default_service and default_match; an
unknown model is 404, and the listing is cached for 15 s.
Response fields: fairshare live and capacity discovery
GET /api/v1/fairshare/live: algorithm; max_in_flight and
configured_max_in_flight (the pools of enabled models with servers summed,
enforced and configured); models_with_servers, how many models those totals
add up, and models_without_servers, the enabled models left out because they
have no api_base, no enabled endpoint, or a discovered backend with no ready
replica;
hard_ceiling and configured_hard_ceiling; default_model_max_in_flight;
replicas; mode; shared_slots; replica_aware; cluster_in_flight;
global_in_flight, global_queued, global_borrowed; groups, tenants,
keys aggregated across pools; model_in_flight and model_queued maps; and
pools, one per model with model, no_servers, cap, configured_cap, in_flight,
cluster_in_flight, queued, borrowed, held (slots this gateway holds
for sessions' next steps, counted in in_flight; 0 in shared mode, where they
are held in Redis) and its group, tenant and key rows.
scope says whose counts these are: cluster (every live gateway, merged, with
replicas_reporting and state_age_ms) or replica (the answering gateway
alone, with scope_reason when shared mode could not read the others).
replica_in_flight and replica_queued, top level and per pool, are always
the answering gateway's own.
Rows carry weight, in_flight, queued, served_tokens, share_score
(served tokens over weight, which the scheduler minimizes), weight_share and
expected_slots; groups add slot_cap and borrowed, tenants and keys
max_in_flight.
GET /api/v1/capacity/discovery: enabled, interval_secs,
namespaces, default_service, replicas, mode, and models, each with
model_id, model_name, enabled, static_max_in_flight,
enforced_max_in_flight, cluster_in_flight, in_flight, and status:
source, namespaces, namespace, service, ready_replicas,
per_replica_max_in_flight (null when endpoints differ),
per_replica_source (configured, endpoint, or both), replica_capacity,
headroom, derived_max_in_flight, effective_max_in_flight, state
(discovered, stale, fallback), last_refresh, last_success, reason.
GET /api/v1/capacity/services: services (service, namespace,
ready), reason when empty or incomplete, errors per namespace,
default_service, default_match.
Settings & alerting
Settings live in Postgres; the data plane picks up a change within 15 s.
| Method | Path | What it does |
|---|---|---|
GET / PUT | /api/v1/settings/alerts | Alert channels |
POST | /api/v1/settings/alerts/test | Send a test alert on each channel |
GET / PUT | /api/v1/settings/auto-router | auto routing and classifier |
POST | /api/v1/router/simulate | Dry-run the router for a prompt |
GET | /api/v1/router/readiness | Lint the routing configuration |
GET / PUT | /api/v1/settings/boons | Global boon switches and settings |
GET / PUT | /api/v1/settings/knowledge | Retrieval and indexing |
GET / PUT | /api/v1/settings/energy | Energy accounting |
POST | /api/v1/settings/energy/test | Run the power queries once, saving nothing |
GET | /api/v1/settings/compressor | Probe the compression sidecar |
GET / PUT | /api/v1/settings/charo | Playground assistant; PUT replaces the whole object |
GET / PUT | /api/v1/settings/usage-retention | Raw usage retention in days |
Every field in the table below is optional on PUT.
| Section | Field | Default | Notes |
|---|---|---|---|
| Alerts | slack_webhook_url | none | Write-only (slack_webhook_set); clear_slack_webhook: true removes it |
| Alerts | min_interval_secs | 300 | Quiet period |
| Alerts | email | none | smtp_host, smtp_port (587), username, smtp_password, from_address, recipients, starttls (true); omitting it turns email off |
| Alerts | email.smtp_password | none | Write-only (password_set); clear_smtp_password: true removes it |
| Auto router | classifier_enabled | false | Ask a small model for intent tags |
| Auto router | classifier_model | null | "" clears |
| Auto router | classifier_timeout_ms | 250 | |
| Auto router | capacity_weight, cost_weight, tag_weight | 0.6, 0.4, 0.5 | Each 0–1 |
| Auto router | default_soft_cap | 8 | Assumed concurrency without max_in_flight |
| Auto router | temperature | 0.0 | 0–2; 0 picks the best score |
| Auto router | difficulty_enabled | false | Tier filter |
| Auto router | tier_source | hybrid | hybrid, derived, declared |
| Auto router | messages_default_model | null | Served for an unknown /v1/messages model; "" clears |
| Auto router | verdicts_default_model | null (auto) | Answers a /v1/verdicts request that names no model; "" clears |
| Boons | vision_enabled, vision_fallback_model | false, null | |
| Boons | vision_describe_prompt, vision_max_images, vision_timeout_ms | built-in, 6, 30000 | |
| Boons | structured_output_enabled, structured_output_fixer_model | false, null | "" repairs with the request's model |
| Boons | structured_output_max_repair_attempts, structured_output_timeout_ms | 1, 30000 | Attempts at most 3 |
| Boons | tool_loop_enabled, tool_loop_max_turns | false, 4 | Turns at most 8 |
| Boons | tool_loop_tool_timeout_ms, tool_loop_deadline_secs | 30000, 300 | Deadline 1–3600 |
| Boons | tool_loop_nudge | built-in | "" restores the default |
| Boons | compression_enabled, compression_min_tokens | false, 128 | |
| Boons | compression_max_segments, compression_max_lossy_segments | 64, 4 | Per request |
| Boons | compression_original_ttl_secs | 3600 | Redis TTL of stashed originals |
| Boons | compression_code_compaction, compression_dedup, compression_compact_logs, compression_allow_lossy | false | Global defaults a tenant policy overrides |
| Boons | compression_neural_keep_ratio | 0.5 | Above 0, at most 1 |
| Boons | image_generation_enabled, image_generation_model | false, null | An image model |
| Boons | image_generation_allowed_sizes | ["512x512", "1024x1024"] | Empty restores the default |
| Boons | image_generation_max_images_per_request, image_generation_timeout_ms | 2, 120000 | Images at most 4 |
| Boons | image_generation_tool_description | built-in | "" restores the default |
| Boons | speculation_enabled, speculation_draft_model, speculation_classify_model | false, null, null | |
| Boons | speculation_verify_model | null | Stored but unused |
| Boons | speculation_agree_min, speculation_lp_min | 0.5, -1.0 | Ship floors |
| Boons | speculation_abort_agree, speculation_abort_lp | 0.45, -1.6 | Abort floors |
| Boons | speculation_first_chunk_tokens, speculation_chunk_tokens, speculation_decide_by_tokens, speculation_max_draft_tokens | 80, 250, 450, 2048 | |
| Boons | speculation_pace_ms, speculation_timeout_ms | 9, 45000 | |
| Boons | speculation_draft_chat_template_kwargs | null | {} clears |
| Boons | speculation_category_gates | [] | [{tag, speculate, agree_min, lp_min}] |
| Boons | speculation_unlisted_categories_speculate | true | |
| Boons | speculation_verify_url_template | "" | With {upstream} and {model} |
| Boons | web_search_enabled, web_search_tool | false, null | A search model; "" clears it |
| Boons | web_search_max_results, web_search_max_searches_per_request, web_search_timeout_ms | 5, 3, 15000 | Results at most 10, searches at most 8 |
| Boons | web_search_tool_description | built-in | "" restores the default |
| Knowledge | enabled | false | Retrieval on or off |
| Knowledge | top_k, min_score, max_context_tokens, query_turns | 5, 0.35, 1500, 2 | |
| Knowledge | embed_timeout_ms, query_cache_ttl_s | 1500, 600 | |
| Knowledge | max_upload_bytes | 10485760 | At most 50 MiB |
| Knowledge | max_chunks_per_collection, index_batch_size, index_timeout_ms, index_stale_after_secs | 100000, 32, 30000, 1800 | |
| Knowledge | debug_snapshot | false | Record retrieved text in spans |
| Energy | enabled | false | |
| Energy | prometheus_url, model_power_query, power_query | unset | Where power is read, per model and per node |
| Energy | model_power_label | model | The label naming the model in model_power_query |
| Energy | model_requests_query | unset | Requests in progress per model on its servers, from every client |
| Energy | poll_interval_secs, energy_cost_per_kwh, carbon_g_per_kwh, pue | 60, 0.1, 400, 1.0 | |
| Assistant | enabled, brain_model, tools_enabled | true, null, {} | |
| Assistant | bench_max_concurrency, bench_max_duration_s, bench_max_requests | 40, 120, 500 | |
| Retention | days | OBLETH_USAGE_RETENTION_DAYS | 1–36500; GET adds configured |
Task pages: Alerting, Auto model routing, Boons, Speculation, Knowledge base, Energy accounting, the Playground assistant.
GET /api/v1/settings/auto-router also returns available_tags.
/router/simulate takes prompt or messages, and optionally max_tokens,
tenant_id, effort, needs_function_calling, needs_tool_choice,
needs_response_schema, weight overrides, busyness, uniform and
classify; it returns the scored candidates and rejections without sending
traffic. /router/readiness returns findings (severity, code, title,
detail, models), pool_size, classifier_active, difficulty_enabled.
/settings/energy/test takes prometheus_url and any of power_query,
model_power_query, model_power_label and model_requests_query, and
returns cluster_watts and node_count, plus models (total_watts,
series, matched, unmatched_series, requests_in_progress) when a
per-model query is given. /settings/compressor returns configured,
url, reachable, model, revision, error.
Slurm provisioner
| Method | Path | What it does |
|---|---|---|
GET / PUT | /api/v1/settings/slurm | Slurm connection and provisioner status |
POST | /api/v1/settings/slurm/test | Check JWT expiry and slurmrestd reachability |
GET | /api/v1/settings/slurm/resolved | Settings with the decrypted JWT, for the provisioner |
GET | /api/v1/slurm/resources | Partitions with their allowed and denied accounts, nodes with state and allocation, accounts, QoS and associations; empty when Slurm is off |
GET | /api/v1/deployments/launches | Launch history of Slurm replicas, newest first |
GET | /api/v1/managed | All launch specs |
GET | /api/v1/models/{id}/managed | One model's spec, or null |
PUT | /api/v1/models/{id}/managed | Create or replace the spec |
DELETE | /api/v1/models/{id}/managed | Remove the spec; replicas drain |
PATCH | /api/v1/models/{id}/managed/provision-error | Set or clear ({"error": null}) the last submit error |
GET | /api/v1/replicas | Replicas across all models |
PATCH | /api/v1/replicas/{id} | Update state, message, nodes, endpoint_id |
DELETE | /api/v1/replicas/{id} | Delete a replica row |
POST | /api/v1/replicas/{id}/restart | Cancel and resubmit the job |
GET | /api/v1/models/{id}/replicas | One model's replicas |
POST | /api/v1/models/{id}/replicas | Register a replica: slurm_job_id, port_base |
POST | /api/v1/models/{id}/replicas/clear-lost | Delete the model's lost rows |
GET | /api/v1/recipes | List saved recipes |
POST | /api/v1/recipes | Create: name, body, author |
PUT | /api/v1/recipes/{id} | Update a recipe |
DELETE | /api/v1/recipes/{id} | Delete a recipe |
Task page: Slurm provisioning. Replica
state is pending, starting, healthy, draining or lost.
PUT /api/v1/settings/slurm takes enabled, slurmrestd_url,
slurmrestd_api_version (default v0.0.40), slurm_user, slurm_jwt
(omit or "" keeps it), node_aliases ([{host, ip}], the full list),
cluster_defaults (cache_dir, images_dir, log_dir, setup and an
images map of engine to image; omit to keep) and hf_token (omit or null
keeps it, "" clears it). GET returns jwt_set, jwt_last4,
cluster_defaults, hf_token_set and hf_token_last4, plus the provisioner's
provisioner_running, provisioner_last_seen_secs, version, git SHA, build
time, provisioner_tick_status (ok, idle, error),
provisioner_tick_detail, provisioner_last_ok_secs and
provisioner_held_secs. The test returns jwt (set, expired,
expires_at, expires_in_secs) and ping (ok, status_code,
latency_ms, error). /settings/slurm/resolved returns the JWT and the
Hugging Face token in full, for the provisioner.
GET /api/v1/deployments/launches takes model_id, recipe_id and limit
(50, at most 500). Each row is one replica's launch, kept after the replica is
gone: a snapshot of the spec, the allocated nodes, submitted_at,
started_at, healthy_at, ended_at, and end_state (Slurm's own, such as
TIMEOUT or OUT_OF_MEMORY, gone, or cancelled:*) with end_reason, plus the computed
queued_secs, load_secs and ran_secs.
Launch spec (PUT …/managed):
| Field | Default | Notes |
|---|---|---|
partition | required | |
serving_port | required | First port of the replica's window; exported as OBLETH_SERVING_PORT |
enabled | true | false drains to zero |
gres, nodes | "", 1 | |
cpus_per_task, mem | partition default | mem 0 is all of each node's memory |
image, preamble, launch_command | "" | Container image, setup lines, command |
script_body | "" | Full job script; overrides the three above |
log_output_dir | "" | Folder for each job's <job name>-<job id>.out and .err; empty is the job's working directory |
health_path | /health | |
target_replicas, min_replicas | 2, 1 | |
max_job_failures | 0 | Stop after this many consecutive lost replicas; 0 is unlimited |
account, qos, time_limit, constraints, exclude | unset | Slurm options |
launcher_spec | unset | Free-form metadata |
Knowledge, MCP, audit, and backup
| Method | Path | What it does |
|---|---|---|
POST | /api/v1/knowledge/collections | Create: name, description, embedding_model |
GET | /api/v1/knowledge/collections | List collections |
GET | /api/v1/knowledge/collections/{id} | Get a collection |
PUT | /api/v1/knowledge/collections/{id} | Update name, description, embedder, chunking |
DELETE | /api/v1/knowledge/collections/{id} | Delete a collection and its documents |
GET | /api/v1/knowledge/collections/{id}/documents | List documents |
POST | /api/v1/knowledge/collections/{id}/documents | Upload a base64-encoded document |
POST | /api/v1/knowledge/collections/{id}/reindex | Re-index every document |
POST | /api/v1/knowledge/collections/{id}/search | Retrieval preview |
DELETE | /api/v1/knowledge/documents/{id} | Delete a document |
POST | /api/v1/knowledge/documents/{id}/reindex | Re-index one document |
POST | /api/v1/mcp-servers | Register: name, upstream_url, optional auth_header |
GET | /api/v1/mcp-servers | List servers |
GET | /api/v1/mcp-servers/{id} | Get a server |
PUT | /api/v1/mcp-servers/{id} | Update URL, key, enabled, or rename |
DELETE | /api/v1/mcp-servers/{id} | Delete; removed from every model's tool_servers |
GET | /api/v1/audit | Audit rows, newest first, filtered |
GET | /api/v1/backup/export | Export configuration as JSON |
POST | /api/v1/backup/restore | Restore a backup; body up to 64 MiB |
Knowledge
PUT also takes chunk_tokens (400) and
chunk_overlap_tokens (50, less than chunk_tokens). A collection reports
indexed_embedding_model, embedding_dim, needs_reindex, chunk_count,
estimated_bytes. Uploads take title, filename and content_base64 for a
UTF-8 .md, .txt or .csv file within max_upload_bytes; a document's
status goes pending, indexing, ready or failed. Search takes
query and top_k and returns hits with score, text and would_inject;
it is 400 until the collection is indexed. See
Knowledge base.
MCP servers
A name is 1–64 letters, digits, ., _ or -, starting
with a letter or digit. PUT requires upstream_url and takes
auth_header (omit or "" keeps it), clear_auth: true to remove it,
enabled, and name (a rename carries into every model's grant). Responses
show auth_header_set. See MCP gateway.
Audit
Rows have id, ts, actor, action, entity_type,
entity_id, detail. See Audit log.
| Query param | Default | Notes |
|---|---|---|
limit | 100 | 1–1000 |
actor, entity_type, entity_id | unset | Exact match |
action | unset | One action or a comma-separated list |
since, until | unset | RFC 3339; since inclusive, until exclusive |
q | unset | Free text over actor, action, entity id and detail |
before_id | unset | Page back from this row id |
Backup
A backup holds fairshare groups, tenants, keys (hashes only),
models, endpoints, MCP servers and every settings row, not knowledge
collections. Secrets are carried as stored; restore refuses a backup whose
key_check does not match the target's OBLETH_ENCRYPTION_KEY, warns on an
API-key pepper mismatch, and returns inserted and updated counts plus
warnings. See Backup and restore.
