Skip to content

Latest commit

 

History

History
426 lines (356 loc) · 24 KB

File metadata and controls

426 lines (356 loc) · 24 KB

API Reference

Every route is mounted under /api. JSON unless otherwise noted; SSE endpoints emit text/event-stream; chat turns use a WebSocket described in Chat (WebSocket).

Interactive docs (Scalar) live at /api/docs when enable_api_docs=true (or OCTOP_ENABLE_API_DOCS=1). The schema is at /api/openapi.json.

Authentication

Header Value
Authorization Bearer <access_token> from POST /api/auth/login

Tokens expire after OCTOP_ACCESS_TOKEN_TTL seconds (default 24 h). Rotating the JWT secret (octop admin rotate-jwt-secret) invalidates every outstanding token immediately. Login attempts are rate-limited (OCTOP_LOGIN_MAX_ATTEMPTS / OCTOP_LOGIN_LOCKOUT_SECONDS); the admin can clear the lockout with POST /api/users/{id}/unlock-login.

Auth column legend

  • public — no token required.
  • user — any logged-in account.
  • owner — same user that owns the resource (or admin).
  • admin — admin role required.

Public endpoints (no token)

/api/docs, /api/openapi.json, /api/health, /api/setup/*, /api/auth/login, /api/auth/oidc/status, /api/auth/oidc/start, /api/auth/oidc/callback, /api/auth/oidc/exchange, /api/connectors/oauth/callback, and /api/internal/mcp/*. All other routes are JWT-gated by api/middleware/jwt_auth.py; the setup lockdown middleware (api/middleware/setup_lockdown.py) additionally blocks non-setup routes until the wizard finishes.

Setup & auth

Method Path Auth Notes
GET /setup/status public {required, completed, has_admin}
GET /setup/presets public Provider templates for the wizard
POST /setup/begin public Begin a wizard session (no password)
POST /setup/verify-password public Exchange the wizard password for a session token
GET /setup/validate-token public Check a wizard session token
POST /setup/initial-admin public body {username, password, display_name?, email?}201
POST /setup/resume-wizard public Issue a fresh wizard token mid-setup
POST /setup/test-provider public Ping a provider draft (kind/base_url/api_key/model)
POST /setup/finish public Finalise setup and unlock the rest of the API
POST /auth/login public body {username, password} (username may be email) → {access_token, role, user, ...}
GET /auth/oidc/status public OIDC login availability and provider display name
POST /auth/oidc/start public body {redirect_after?} → identity-provider authorization URL
GET /auth/oidc/callback public Identity-provider callback; redirects to dashboard login completion
POST /auth/oidc/exchange public body {code} → same JWT response as /auth/login
GET /auth/oidc/config admin OIDC provider configuration and callback URL; client secret is omitted
PUT /auth/oidc/config admin Write OIDC provider configuration; client_secret is write-only
POST /auth/oidc/config/test admin Verify configured discovery metadata and JWKS endpoint
POST /auth/logout user 204
GET /auth/me user {id, username, role, display_name, locale, ...}
PATCH /auth/me user body {display_name?, locale?, ...}
POST /auth/change-password user body {old_password, new_password}204
GET /health public {status: "ok", version}

Users (admin)

Method Path Auth Notes
GET /users admin [{id, username, role, display_name, email, enabled, ...}]
POST /users admin body {username, password, role, display_name?, email?}201
GET /users/{id} admin full user row
PATCH /users/{id} admin body subset of {role, display_name, email, enabled, locale}
POST /users/{id}/reset-password admin body {new_password}204
POST /users/{id}/unlock-login admin 204 (clears the lockout)
DELETE /users/{id} admin 204

Agents

Method Path Auth Notes
GET /agents user [{id, agent_id, name, persona_mbti, state, unread_count, ...}]
POST /agents user body {name, persona_mbti?, default_model?, system_prompt?, description?, icon?, template_name?, config?}201
GET /agents/{id} owner full agent row
PATCH /agents/{id} owner body subset of create body
DELETE /agents/{id} owner 204
POST /agents/{id}/start owner 204
POST /agents/{id}/stop owner 204
POST /agents/{id}/reload owner 204 (rebuild harness runtime)
POST /agents/{id}/read owner 204 (mark unread badge cleared)
GET /agents/{id}/status owner {state, last_error?, ...}
POST /agents/from-expert/{expert_id} user body {name, ...}201 (creates from bundled expert template)
GET /agents/{id}/tool-settings owner built-in + installed plugin tools with enable / disableable / available flags
PUT /agents/{id}/tool-settings owner body {disabled_builtin: string[], plugins?} — persists denylist + plugin flags (hot-sync, no reload)
PATCH /agents/{id}/tool-settings/{tool_name} owner body {enabled, source, plugin_id?} — toggle one tool (hot-sync)

Chat (WebSocket)

Path Auth Notes
WS /agents/{id}/chat/ws?token=<jwt> owner Primary dashboard turn endpoint. Send {"type":"user_turn", ...} frames; server replies with harness stream chunks ending in {"type":"done"} or {"type":"error","message":"..."}. {"type":"ping"}{"type":"pong"}. {"type":"subscribe","thread_id"}{"type":"turn_status","thread_id","active"} (attach to an in-flight turn without cancelling on disconnect). {"type":"cancel","thread_id"} stops the active turn (explicit stop; disconnect alone does not cancel).
POST /agents/{id}/chat/polish owner body {text, default_model?}{text} (one-shot prompt refinement)
POST /agents/{id}/chat/hitl/resume owner body {thread_id, decisions: [...]} → SSE chunk stream; finishes with {"type":"done"}

Legacy SSE

The previous POST /agents/{id}/chat/stream is gone. The dashboard streams turns over the WebSocket above; HITL resume stays on SSE because each request is a one-shot continuation.

Threads & history

Method Path Auth Notes
GET /agents/{id}/chat/sessions owner [{id, thread_id, title, archived, last_active, unread, ...}]
POST /agents/{id}/chat/sessions owner body {session_key?}{thread_id, session_key}
PATCH /agents/{id}/chat/sessions/{thread_id} owner body {title?, pinned?} → updated row
DELETE /agents/{id}/chat/sessions/{thread_id} owner 204 (archives the active row)
GET /agents/{id}/chat/sessions/{thread_id}/history owner paginated message history; turn_active tells a reconnecting client whether to re-subscribe over the chat WebSocket

Channels

Method Path Auth Notes
GET /agents/{aid}/channels owner list of channel rows
POST /agents/{aid}/channels owner body {kind, name, config}201
GET /agents/{aid}/channels/{cid} owner channel row
PATCH /agents/{aid}/channels/{cid} owner body subset → updated row
DELETE /agents/{aid}/channels/{cid} owner 204
POST /agents/{aid}/channels/{cid}/test owner {ok, error?} (instantiate → start → stop)
POST /agents/{aid}/channels/probe owner {ok, reason?, detail?} — preflight a candidate config
POST /agents/{aid}/channels/{platform}/qrcode/generate owner platform-specific bot creator (wecom, weixin, feishu, yuanbao)
POST /agents/{aid}/channels/{platform}/qrcode/poll owner poll bot creator state
POST /agents/{aid}/channels/{platform}/bot-creator/start owner start a bot-creator flow
POST /agents/{aid}/channels/{platform}/bot-creator/poll owner poll progress
POST /agents/{aid}/channels/{platform}/bot-creator/stop owner stop an in-flight bot creator

Cron

Method Path Auth Notes
GET /settings/timezone user process-level {timezone} from default_timezone
GET /cron/settings user compat alias of /settings/timezone
GET /agents/{aid}/cron owner list of cron rows
POST /agents/{aid}/cron owner body {trigger, prompt, session_key?, fresh_thread?, model?, task_type?}201
GET /agents/{aid}/cron/{cid} owner cron row
PATCH /agents/{aid}/cron/{cid} owner body subset → updated row
DELETE /agents/{aid}/cron/{cid} owner 204
POST /agents/{aid}/cron/{cid}/run-now owner 204 (fire immediately, off-schedule)

task_type is "text" (push prompt directly to the session) or "agent" (run the prompt through the LLM and push the reply). Default: "agent". trigger accepts cron expressions ("0 9 * * *") plus the interval:N / date:ISO8601 aliases documented in infra/cron/trigger.py. prompt must be non-empty and ≤ 2000 characters.

Providers

Method Path Auth Notes
GET /providers user providers visible to the user (own + shared)
POST /providers user body {name, kind, base_url?, api_key?, model?, ...}201
PATCH /providers/{id} owner body subset → updated row
DELETE /providers/{id} owner 204 (refuses if any agent references it)
POST /providers/{id}/test user {ok, latency_ms?, error?} (one-token ping with 10 s timeout)
POST /admin/providers admin same body as user POST; row has user_id = NULL
PATCH /admin/providers/{id} admin as user PATCH but works on shared rows
DELETE /admin/providers/{id} admin 204

Models

Method Path Auth Notes
GET /models/presets user provider templates from harness-agent
GET /models user resolved models across enabled providers
GET /models/active user {provider_name, model}
PUT /models/active admin body {provider_name, model}

Voice

Method Path Auth Notes
GET /voice/presets user voice provider presets
GET /voice/providers user user's voice providers
GET /voice/active user active TTS / STT configuration
PUT /voice/active user update active voice configuration
POST /voice/stt user body {audio, format?, language?}{text, segments?}
POST /voice/tts user body {text, voice?, format?} → audio bytes
GET/POST/PATCH/DELETE /admin/voice/providers admin admin voice provider CRUD

MBTI & personas

Method Path Auth Notes
GET /mbti/codes user [{code: "INTJ", ...}, ...] (16 codes + _default)
GET /mbti/codes/{code} user full profile (dimensions, behaviour, UI metadata)
GET /mbti/preview/{code} user rendered persona template (legacy /api/personas/{code})
PUT /agents/{aid}/mbti owner body {code} → apply persona and reload
GET /personas user [{code}, ...] (compat shim)
GET /personas/{code} user rendered template (compat shim)

Persona content lives in src/octop/infra/agents/mbti_profiles.py — see Personas.

Experts

Method Path Auth Notes
GET /experts user bundled expert catalog (locale-aware)
GET /experts/{expert_id} user full expert template (SOUL.md, skills, files)
POST /agents/from-expert/{expert_id} user body {name, locale?, ...}201

Bundled experts live in src/octop/infra/agents/experts/library/ (en/zh divisions); the catalog is locale-aware via Accept-Language / user preference.

Workspace, skills, subagents, memory, files

Method Path Auth Notes
GET /agents/{aid}/workspace/tree owner list dir (default .)
GET /agents/{aid}/workspace/file owner read a content file
PUT /agents/{aid}/workspace/file owner write a content file (via BackendWorkspace)
DELETE /agents/{aid}/workspace/file owner delete a content file
POST /agents/{aid}/workspace/rename owner rename / move
POST /agents/{aid}/workspace/upload owner multipart upload → backend
GET /agents/{aid}/workspace/download owner download a file
GET /agents/{aid}/workspace/glob owner glob backend paths
GET /agents/{aid}/workspace/grep owner grep backend files
GET /agents/{aid}/workspace/... owner see api/routers/workspace.py
GET /agents/{aid}/skills owner list installed skills
PUT /agents/{aid}/skills/{slug} owner enable / disable a skill
GET /agents/{aid}/skills/hub/search user Skill Hub search
GET /agents/{aid}/skills/hub/rankings user Skill Hub rankings
POST /agents/{aid}/skills/hub/install owner body {slug, version?}201
GET /subagent-catalog/divisions user bundled subagent divisions
GET /subagent-catalog user bundled subagent catalog
GET /subagent-catalog/{slug} user full subagent definition
GET /agents/{aid}/subagents owner installed subagents for an agent
POST /agents/{aid}/subagents owner install a bundled subagent
GET /agents/{aid}/heartbeat-config owner read heartbeat YAML
PUT /agents/{aid}/heartbeat-config owner write heartbeat YAML
GET /agents/{aid}/memory/daily owner list daily memory files
GET /agents/{aid}/memory/daily/{filename} owner read one daily memory
DELETE /agents/{aid}/memory/daily/{filename} owner delete one daily memory
GET/POST /memory/... user memory API (dashboard memory tab)

ACP (Agent Client Protocol)

Method Path Auth Notes
GET /acp user current user's global runner list
PUT /acp user replace global runners
GET /acp/{runner_name} user one runner
PUT /acp/{runner_name} user upsert one runner
DELETE /acp/{runner_name} user delete a custom runner (built-ins are protected)
GET /agents/{aid}/acp owner global runners + this agent's tool_enabled flag
PUT /agents/{aid}/acp owner update tool_enabled and optionally the global list
PUT /agents/{aid}/acp/tool owner toggle the acp_runner tool only

See ACP integration for the runner object schema and the Zed setup example.

Storage backends

Method Path Auth Notes
GET/POST/PATCH/DELETE /storage-backends user per-user remote backend connections
GET/POST/PATCH/DELETE /admin/storage-backends admin admin-managed backends

Host filesystem (dashboard)

Browse the host OS directory tree when configuring a local backend root_dir (local_shell / filesystem). Authenticated users only; sensitive mounts (/proc, /sys, /dev, /etc, /root on POSIX) are rejected, except the process home and its subdirectories (so a server running as root may use /root as the default root_dir). Listing is single-level and capped; write probe runs only for non-/ paths.

Method Path Auth Notes
GET /filesystem/dirs?path=<abs> user {path, entries: [{path, name}]} — one directory level
POST /filesystem/probe user body {path}{ok, path?} or {ok: false, code, detail?} (not_directory, permission_denied, write_failed, not_allowed)
POST /filesystem/mkdir user body {path, base_name?}{path, name} — create child dir (base_name defaults to New Folder; collisions become Name (2), …)
POST /filesystem/rename user body {path, new_name}{path, name} — rename basename only

Connectors & OAuth

Method Path Auth Notes
GET /connectors/catalog user connector catalog (Notion, Figma, …)
GET /connectors/test-credentials user preflight credentials
GET /connector-instances user list instances
POST /connector-instances user create instance
GET/PATCH/DELETE /connector-instances/{id} user CRUD on an instance
POST /connector-instances/{id}/test user test a configured instance
POST /connector-instances/{id}/refresh user refresh OAuth tokens
GET /connectors/auth/{kind}/info user auth flow info
GET /connectors/auth/{kind}/authorize-url user build the authorize URL
POST /connectors/auth/{kind}/exchange-code user exchange auth code
POST /connectors/oauth/{kind}/start user start an OAuth flow
GET /connectors/oauth/callback public OAuth redirect target
GET /connectors/oauth/pending/{state_id} user poll the OAuth result

Internal MCP (harness agents)

Method Path Auth Notes
POST/GET/… /internal/mcp/* public (mTLS / network-isolated) MCP gateway used by harness agents (not the dashboard)

Observability & security

Method Path Auth Notes
GET/PUT /admin/observability admin Langfuse configuration (host, project, env)
GET /admin/security admin global security policy
PUT /admin/security admin update global policy
GET /admin/security/tool-guard/rules admin active command guard rules
GET /admin/security/tool-guard/rules/raw admin editable YAML
PUT /admin/security/tool-guard/rules/raw admin save YAML
POST /admin/security/tool-guard/rules/reset admin reset to shipped defaults
GET /admin/security/defaults admin defaults + rule catalogs

TLS (Let's Encrypt)

Method Path Auth Notes
GET /admin/tls/status admin current cert + issuance task status
POST /admin/tls/preflight admin preflight (port 80, DNS)
POST /admin/tls/issue admin start HTTP-01 issuance

GET /.well-known/acme-challenge/{token} is the HTTP-01 challenge endpoint (public, mounted directly in api/app.py).

Browser, terminal, uploads

Method Path Auth Notes
WS/POST/GET/… /agents/{aid}/terminal owner AI-assisted remote PTY
GET /agents/{aid}/terminal/context owner recent terminal context for the AI helper
WS/POST/GET/… /browser/... user remote Playwright sessions, screenshots, live streams
POST /agents/{aid}/upload user multipart upload → {workspace}/inbound/
POST /agents/{aid}/files/access-urls user refresh inbound media URLs (signed)
GET /agents/{aid}/files/{path} owner read an inbound file

Updates, ollama, i18n, plugins, slash, preferences

Method Path Auth Notes
GET/POST /update/status, /check, /upgrade, /progress, /restart admin in-place server update flow
GET/POST/DELETE /ollama/... user Ollama model discovery + downloads
GET /i18n/tools user server-owned tool display names (locale-aware)
GET /i18n/locales public available locales + fallback chain
GET /i18n/locales/{locale}/{namespace} public one namespace bundle (errors, tools, channel, slash)
GET/POST /preferences user UI preferences (per-user key/value)
GET /slash/commands user slash command catalog for the composer menu
GET/POST /plugins user installed plugin list / install flow
POST /plugins/reload admin (plugins) reload plugins from disk into process
PATCH /plugins/{id} admin (plugins) enable/disable plugin ({ "enabled": bool })
GET /plugins/{id}/ui/{path} user serve prebuilt plugin UI assets (ui/dist/…)
DELETE /plugins/{id} admin (plugins) uninstall plugin

Usage & admin

Method Path Auth Notes
GET /usage/summary user token usage summary for the current user
GET /admin/usage admin global token usage summary
GET /admin/overview admin {user_count, agent_count, ...}
GET /admin/audit-log admin recent audit rows
GET /admin/metrics admin {messages_total, stream_errors_total, cron_runs_total, cron_errors_total, agent_active, ...}

Envs

Method Path Auth Notes
GET/PUT /agents/{aid}/envs owner env-var preset for an agent's tool calls
GET/POST/DELETE /envs/presets user reusable presets

Error envelope

Every non-2xx JSON response uses:

{ "error": { "code": "AGENT_NOT_FOUND", "message": "...", "details": { } } }

code matches the ErrorCode enum in octop.infra.errors; the server-localized message is rendered by OctopError.to_envelope with the locale from Accept-Language (falling back to en). The dashboard mirrors every code under apiErrors.* in dashboard/src/locales/{en,zh}.json.

Code HTTP Meaning
AUTH_FAILED 401 Bad credentials
TOKEN_EXPIRED 401 JWT past its TTL
LOGIN_LOCKED 423 Too many failed attempts — wait login_lockout_seconds or call /users/{id}/unlock-login
SETUP_REQUIRED 409 Initial admin not yet created (or wizard not finished)
FORBIDDEN 403 Authenticated but not allowed
NOT_FOUND 404 No such row / route
USER_DISABLED 403 Account flag flipped off
USERNAME_TAKEN 409 Conflict on users.username
AGENT_NOT_FOUND 404 Agent row missing or owned by another user
AGENT_FAILED 500 Runtime errored during a call
AGENT_BUSY 409 Operation refused while another is in flight
PROVIDER_NAME_TAKEN 409 Conflict on providers.name
PROVIDER_NOT_VISIBLE 400 Agent config references a provider the user can't see
PROVIDER_REFERENCED 409 Delete blocked because agents still reference the row
PROVIDER_TEST_FAILED 400 /providers/{id}/test failed
CHANNEL_KIND_UNSUPPORTED 400 kind not in registered builders
CHANNEL_INVALID_CREDENTIALS 400 Channel config rejected by the platform
CHANNEL_PROBE_INCOMPLETE 400 Probe couldn't reach the platform
CRON_TRIGGER_INVALID 400 Trigger string did not parse
CRON_PROMPT_INVALID 400 Empty or too-long prompt
SLASH_UNKNOWN 400 /<cmd> is not a registered handler
SLASH_BAD_ARGS 400 Slash handler rejected its arguments
ATTACHMENT_UNSUPPORTED_TYPE 400 Chat / inbound attachment rejected (media type not allowed)
ATTACHMENT_TOO_LARGE 413 Chat / inbound attachment exceeds the configured size limit (max_mb in details)
WORKSPACE_PATH_INVALID 400 Path outside the agent's workspace
STORAGE_BACKEND_UNREACHABLE 502 Remote backend connect / list failed
CONNECTOR_OAUTH_FAILED 400 OAuth flow could not complete
INTERNAL_ERROR 500 Unhandled exception (logged with traceback)