Skip to content

HTTP API

All routes are under /v1. Send credentials as Authorization: Bearer <token>. JSON in, JSON out. Errors are {"detail": "..."}.

Auth column: admin needs PLNT_ADMIN_TOKEN; tenant accepts the admin token or that tenant’s pk_… key; none is public.

MethodPathAuth
GET/healthnone{ok, version, dev}
GET/whoamiany{role: "admin"} or {role: "tenant", tenant_id}
GET/bundlesnoneCatalog: slug, version, description, tools, config_schema, required secrets.
MethodPathAuthBody → Response
POST/tenantsadmin{id, name?} → 201 {tenant, api_key} (key shown once; 409 if the id exists)
GET/tenantsadmin{tenants: [...]}
GET/tenants/{t}tenantSummary plus installs, secret names, model
DELETE/tenants/{t}admin204. Deletes all of the tenant’s data.
POST/tenants/{t}/keysadminRotate → {api_key}. The old key stops working.
MethodPathAuthBody → Response
GET/tenants/{t}/installstenant{installs: [...]}, each with its config_schema and secrets_required
POST/tenants/{t}/installstenant{bundle, config} → 201 install. Catalog slugs only. 422 on invalid config.
PATCH/tenants/{t}/installs/{slug}tenant{enabled?, config?} → install
DELETE/tenants/{t}/installs/{slug}tenant204
MethodPathAuthBody → Response
GET/tenants/{t}/secretstenant{secrets: ["NAME", ...]}: names only
PUT/tenants/{t}/secrets/{NAME}tenant{value} → 204. Names are UPPER_SNAKE.
DELETE/tenants/{t}/secrets/{NAME}tenant204
GET/tenants/{t}/modeltenant{model: {...} | null}
PUT/tenants/{t}/modeltenantModel profile (below) → {model}
DELETE/tenants/{t}/modeltenant204: back to the server default
GET/tenants/{t}/model/healthtenant{ok, reachable, model_present, detail, hint, ...}

Model profile fields: provider (ollama or openai), base_url, model (required); deep_model, api_key_secret (the name of a tenant secret), num_ctx, temperature, max_tokens, timeout, native_tools (auto/on/off), cost_in_per_m, cost_out_per_m.

MethodPathAuthBody → Response
POST/tenants/{t}/sessionstenant{bundle?, user_id?, workspace?} → 201 {session_id}. No bundle starts a parent session; workspace is demo:<name>, a folder, or a git URL (policy); 422 when refused.
GET/tenants/{t}/sessions?limit=100tenant{sessions: [{id, title, workspace, workspace_kind, bundle, mode, user_id, status, created_at}]}
POST/tenants/{t}/sessions/{sid}/messagestenant{text} → 202 {run_id}. 409 if a run is in progress.
GET/tenants/{t}/sessions/{sid}/events?after=Ntenant{events: [...]} with seq > N
GET/tenants/{t}/sessions/{sid}/stream?after=N&until_idle=0tenantServer-sent events. until_idle=1 closes after the next run_finished.
POST/tenants/{t}/sessions/{sid}/killtenant{killed: bool}
GET/tenants/{t}/sessions/{sid}/transcripttenantThe events folded into turns. See Transcript.

Each SSE message has id = the event’s seq, event = its kind, and data = the event as JSON. See Events.

MethodPathAuth
GET/tenants/{t}/usage?since=<unix seconds>tenantTotals (model_calls, prompt_tokens, completion_tokens, cost_usd) and a breakdown by bundle and model.
GET/tenants/{t}/audit?limit=200&action=tenant{events: [...]}: the most recent limit entries, oldest first.

Anonymous, limited to the seeded demo tenants, rate-limited per IP.

MethodPath
GET/playgroundDemo tenants, their agents and config, limits.
GET/playgroundDemo tenants with their workspace (name, file_count, suggested_tasks) and agents, parent, execute_enabled, models, limits.
POST/playground/sessions{tenant, bundle?} → {session_id, token}. No bundle = parent mode. The workspace is the tenant’s own demo; a copy is made per session.
GET/playground/sessions/{sid}/transcript?token=The turns, as above.
POST/playground/sessions/{sid}/messages?token={text} (≤ 500 chars). 429 when rate-limited, 503 when the daily budget is used up.
GET/playground/sessions/{sid}/stream?token=&after=SSE, as above.
POST/playground/sessions/{sid}/kill?token=

The OpenAPI schema is at /openapi.json, and interactive docs at /docs, on any running server.