CLI and API reference

Last updated October 5, 2026

On this page

Draft for review

OrchKernel has two ways in besides the web workspace: the ok command line, and the HTTP API the workspace itself uses. This page lists both in one place. Each line points to the guide page that explains the command or route in full, so use it to find your way, then read the page it names.

The examples use the demo company from the Quickstart: ada the admin, maya the support lead, alice a sales rep, erin the engineering lead, pat the product lead, fiona the founder, and sam, tom and eli. Each person's sign-in token is printed when ok demo serve starts; those tokens are valid for 7 days.

The examples use 127.0.0.1:8080, the address ok demo serve listens on unless you pass --bind. Change the port if you started it elsewhere.

The command line#

Where a command works#

Most ok commands do not talk to a server. They open the state file (the SQLite file that holds the whole company; the demo's is orchkernel-demo/state.db), do their work and write the file back when they finish. A running server keeps the company in memory and writes the same file after each change, so it never sees what a command wrote, and its next write replaces it.

Command Works on Safe while the server runs
ok serve, ok demo serve Starts the server Not applicable
ok backup, ok audit ..., ok doctor Reads the file without writing it, or (ok doctor) only the model settings Yes
ok policy ... --server <url> The running server, with your token in OK_TOKEN Yes
ok tenant ..., ok operator ... A tenancy root's files, or its operator listener with --server See Hosting several organizations
Everything else The state file No. Stop the server first, or use the API

This holds even for commands that only read, such as ok log or ok inbox: they write the file back too.

To run a command on the demo, work in the folder where you ran ok demo (the one that holds orchkernel-demo):

  1. Stop ok demo serve with Ctrl+C. The server writes the file after every change, so nothing is lost.
  2. Run the command with the demo's state file and the person you act as: ok --state orchkernel-demo/state.db --as maya brain query tickets. You should see the demo's five support tickets as JSON, the first one T-1042, "Refund for a double charge on the September invoice".
  3. Start the demo again with ok demo serve. It prints new sign-in tokens and revokes the ones it printed last time. Tokens you issued yourself keep working.

In the demo, ok --state orchkernel-demo/state.db --as ada kill actor:eli run with the server stopped printed "actor:eli engaged", and once the server started, GET /api/policy showed the switch on Eli held by an admin. The same command for Tom run while the server was up also printed "actor:tom engaged" and wrote a kill_switch event to the log, but the server never applied it, and its next write removed it from the file. The event stays in the log, so the log records a switch that never took effect. See Known problems.

Global flags#

Every command takes these, except -V, which only ok itself takes.

Flag Environment variable Default What it does
--state <path> OK_STATE ./.orchkernel/state.db The state file. ok demo ignores it and uses <dir>/state.db.
--as <id> OK_AS your login name ($USER) The person or agent you act as. Every check is made for this actor.
--tenant <name> OK_TENANT none Act on one tenant of a tenancy root. With --server, sent as the x-ok-tenant header.
--tenants-dir <dir> OK_TENANTS_DIR none The tenancy root.
--dev-echo-tools OK_DEV_ECHO_TOOLS off Answer every tool call with an echo and ignore mcp.yaml. Development only.
-h, --help Help for any command or subcommand: ok brain query --help.
-V, --version Prints the version (ok 0.1.0).

Two things catch people out:

  • Without --state, a command in a folder with no .orchkernel/state.db creates a new, empty company there and works on that, with no warning.
  • An --as that names nobody is refused by commands that check what you may do, such as ok brain query and ok kill ("Error: unknown actor nobody"). Others do not check: ok inbox, ok tasks and ok threads print nothing and succeed, and ok log prints the log. Without --as, the command acts as your computer login name, which the demo does not know either.

Environment variables#

The settings a server reads are listed in full in Self-hosting. These are the ones the command line itself reads.

Variable Used by What it does
OK_STATE, OK_AS, OK_TENANT, OK_TENANTS_DIR, OK_DEV_ECHO_TOOLS Every command As the global flags above.
OK_SERVER ok policy The server URL, instead of --server. It must be https, or http to a loopback address such as 127.0.0.1.
OK_TOKEN ok policy with a server Your API token. It is never taken on the command line.
OK_OPERATOR_TOKEN ok tenant and ok operator with --server An operator token.
OK_BIND ok serve Where it listens. ok demo serve ignores it and takes --bind.
OK_SECRETS_KEY ok connection, ok secrets rotate, the server The key that seals connection secrets. See Connections.
OK_DEV_AUTH ok connection, the server Lets connections use plain http to a loopback address. Development only.
ANTHROPIC_API_KEY, OPENAI_API_KEY, GEMINI_API_KEY or GOOGLE_API_KEY, OLLAMA_HOST, OK_LLM_CONFIG ok ask, ok doctor, the server Model providers. See Models.
OK_LLM_CHECK ok serve, ok demo serve 0 skips the key check at start.

Commands#

One line per command; run it with --help for its flags. The last column is the page that explains it.

Getting started and running

Command What it does Page
ok demo [--dir D] [--force] Creates the demo company in ./orchkernel-demo Quickstart
ok demo serve [--dir D] [--bind A] [--dev-auth] Starts the demo's local fakes and server, and prints a token per person Quickstart
ok init [--admin id] [--blueprint b] Creates a state file and its first admin, optionally a whole company Company setup
ok serve Runs the API and the web workspace Self-hosting
ok backup <path> Copies the state file safely, even while the server runs Self-hosting
ok doctor Checks each model key; exits 1 when no provider is set ("FAIL no model provider is configured") Models
ok schemas Prints the JSON Schema of every definition type (agents, skills, packs) Packs and skills

Company, people and tokens

Command What it does Page
ok blueprint list, ok blueprint install <id or file> Lists and installs blueprints Company setup
ok actor list Everyone in the directory Company setup
ok actor add-human <id> --role <r>, ok actor add-team <team:id> Adds a person or a team Company setup
ok actor offboard <id> Offboards a person or agent Company setup
ok actor hire <file>, ok actor trust <id> <tier> Hires an agent from YAML; sets its trust tier Agents
ok directory find [--skills a,b] [--humans], ok directory cards Who can do something; every agent's card Company setup, Agents
ok token issue --for <id> [--ttl] [--label] [--scope] Issues an API token, printed once; valid 30 days unless --ttl says otherwise (7d) Company setup
ok token list, ok token revoke, ok token scope Lists, revokes or narrows tokens Company setup

Agents and work

Command What it does Page
ok ask --agent <id> "<text>" [--plan file] Asks an agent in your own conversation. Needs a model or --plan: the demo's stub planner answers only in the workspace, so without a model key the run fails with "no model provider is configured" Asking agents
ok tasks [--archived], ok runs [--archived] Tasks and runs, live or archived Asking agents
ok tick [--now t] Advances the clock once: triggers, SLAs, stalls, pending runs Asking agents
ok webhook <source> [--payload json] Delivers an event to webhook triggers, unsigned Modules
ok inbox [--all] Your inbox Inbox and approvals
ok approve <id> [--reject] [--note] Decides an approval Inbox and approvals
ok answer <run> "<text>" Answers the question a run waits on Inbox and approvals
ok threads [--show id] Lists threads, or one thread's transcript Threads
ok agent library, ok agent fork <id> --slug <s> The agent library; forks a module's agent Agents
ok playbook show <id> An agent's or skill's playbook Agents
ok playbook propose <id> <file> --reason <r> Proposes a playbook change Changes and evals
ok changes list, review, canary, promote The change pipeline Changes and evals
ok delegate --to <id> [--scope] [--authority] [--budget-cents] [--tools] Delegates authority to someone Delegations

Data

Command What it does Page
ok brain collections, describe <c>, catalog <phrase> Collections, one collection's fields, collections matching a phrase The brain
ok brain query <c>, upsert <c> --fields json, history, rollback, flag, flags Records and their versions The brain
ok brain recall [topics], quarantine, accept <id>, reject-knowledge <id> Knowledge entries Knowledge and decisions
ok schema proposals, apply <id>, reject <id>, steward Schema proposals The brain
ok search "<text>" [--collections] [--kinds] [--limit] Keyword search over what you may read The brain
ok context sources, run, optin, optout, forget Context capture Context capture

Governance and audit

Command What it does Page
ok policy export, check, diff, apply, source Policy as code Policy
ok policy explain [op] [target] --actor <id> What the gate would decide, step by step How the gate decides
ok kill <scope> [--off] Engages or releases a kill switch: global, actor:<id>, tool:<id>, team:<id> Policy
ok log [--type t] [--limit n] The newest events of the whole log Events and audit
ok replay --run <id> (or --task, --thread) One run, task or thread from the log Events and audit
ok audit verify, head, checkpoints Checks the tamper-evident chain Events and audit

Modules, connections and outside agents

Command What it does Page
ok catalog list, upload <file>, retire <id> The modules catalog Modules
ok module list, show, enable, set, pause, resume, reconcile, adopt Installed modules Modules
ok pack list, ok pack install <dir> Packs installed directly Packs and skills
ok connection add, update, set-secret, list, test, remove Connections to outside systems Connections
ok secrets rotate --new-key-env V Re-seals connection secrets under a new key, server stopped Connections
ok gateway agent add, list, show, set Registers and changes outside agents Outside agents
ok gateway token issue <agent> Issues an outside agent's token Outside agents
ok gateway actions --agent, work-sessions --agent An outside agent's actions and work sessions Outside agents
ok gateway callback rotate, test An outside agent's signed callbacks Outside agents

Several organizations on one host

Command What it does Page
ok tenant create, adopt, list, show, set Tenants of a tenancy root Hosting several organizations
ok tenant suspend, resume, delete, undelete A tenant's lifecycle Hosting several organizations
ok tenant admin-token, rotate-key Break-glass token; the tenant's secrets key Hosting several organizations
ok tenant backup, backups, restore, upgrade, check, registry rebuild Backups, upgrades and health Hosting several organizations
ok operator token issue, list, revoke; ok operator rotate-key Operator tokens and key Hosting several organizations

The HTTP API#

This part is for people writing scripts or tools against a running server. Everything the web workspace does, it does through this API.

Base path#

Every route is under /api on the server's own address, for example http://127.0.0.1:8080/api/me. Two exceptions sit at the root: the MCP endpoint POST /mcp for outside agents, and the operator API on its own listener when the server hosts several organizations. Any other path that is not under /api serves the web workspace. An /api path no route serves answers 404 not_found, "no such API route".

Bodies are JSON and need content-type: application/json. GET /api/health needs no token and answers { "ok": true, "dev_auth": false }.

Tokens#

Send a token in every request: Authorization: Bearer <token>. There are no passwords and no cookies.

Token Looks like Reaches
A person's token orchk_... Every /api route, with that person's rights
A scoped token orchk_..., issued with scopes Only the routes its scopes allow (below)
An outside agent's token orchk_ag_... Only the agent-facing gateway routes and /mcp. Anywhere else: 403 agent_token_scope
An operator token orchk_op_... Only the operator API. A tenant's token is 401 there

To get a token for a script from the workspace (alice, in the demo):

  1. Sign in as Alice and open your profile: choose your name, Alice Chen, at the bottom of the sidebar. On a phone, open the menu (the button at the top left) first. The page is titled Profile.
  2. Under Sessions & tokens, choose New token.
  3. In New sign-in token, type a Label ("Report script"), pick Valid for (1 day, 7 days, 30 days or 90 days; 30 days is preselected), then choose Issue token. The sheet Your new token shows it once, under "Copy it now: it is shown once. It expires ..." with a Copy button.
  4. Choose I have copied it. The token now has its own row under Sessions & tokens, with its label, its dates and Revoke.
  5. Try it: curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:8080/api/me returns Alice's directory entry with "admin": false.

An admin issues tokens for others from People: each person has an Issue token button. Another person who tries over the API gets 403 "policy denied token.issue: only admins issue tokens for others". Tokens are valid 30 days unless you choose otherwise; the server keeps only a hash, so a lost token cannot be shown again. The full table of ways to issue and revoke is in Self-hosting.

Scopes. A token can be limited to some routes. The workspace cannot set scopes; use POST /api/tokens with "scopes", or ok token issue --scope with the server stopped. A scope can later be narrowed but never widened. On the profile page a scoped token's row says "limited to read" (or the scopes it has).

Scope Reaches
read Every GET route, as the holder sees it. Not POST reads such as a collection query.
changes POST /changes, and the holder's own proposals: GET /changes, GET /changes/:id/diff, POST /changes/:id/reject.
policy POST /policy/check, POST /policy/diff, POST /policy/proposals, GET /policy/applied, and the holder's own proposals as above. Not /policy/explain.
none Everything the holder may do.

No scope reaches the audit routes. A route outside a token's scopes answers 403 token_scope, "this token's scopes do not reach /brain/collections/:name/query".

Failed tokens. A missing or bad token is 401. From the 61st failed credential in a minute, the address gets 429 rate_limited, "too many failed authentications from this address", with a Retry-After header for the rest of the minute. A valid token from the same address still works, and a request with no token at all is not counted and still gets 401.

Errors and status codes#

An error is JSON with a short code and a sentence:

json
{ "error": "denied", "message": "brain: access denied: ada may not read tickets" }

Some errors add a field: problems (a list of what is wrong) on 422 invalid, approval (its id) on 202 needs_approval, and evals (the case names) on evals_failing and evals_not_run.

Status Codes When
200 Done.
202 needs_approval The action waits for a person. Body: { "error": "needs_approval", "approval": "<id>", ... }. Erin offboarding Eli gets this. Webhook deliveries and loop runs also answer 202, with their own bodies.
400 bad_request A bad query value, such as a cursor the server did not issue ("invalid cursor").
401 unauthorized "missing bearer token", "invalid token", "not a bearer token", or a webhook without a valid signature.
403 denied, not_approver, token_scope, agent_token_scope, not_external_agent, not_owner, ... The policy or a rule refuses you.
404 not_found It does not exist, or you may not see it. The two read the same: "thread nope not found", "unknown actor ghost".
409 conflict and named codes (evals_failing, last_approver, module_paused, ...) The thing is in the wrong state for this.
422 invalid and named codes The request is well formed but not acceptable, such as GET /api/traces?since=yesterday: "since "yesterday" is neither an RFC 3339 time nor a YYYY-MM-DD date".
429 rate_limited Too many failed credentials, or a rate limit in the policy.
503 audit_unavailable and others The server cannot do it now.

Not every error has this shape. A body that is not JSON (400), a JSON body missing a required field (422), a missing content-type (415) and a wrong method (405) answer in plain text or with an empty body, for example "Failed to deserialize the JSON body into the target type: missing field goal". See Known problems.

The named codes each route can return are listed in the For developers section of the page that covers it.

Paging#

Long lists come a page at a time. Ask with ?limit= (default 200, at most 1000). When there is more, the answer carries an x-next-cursor header; send its value back as ?cursor= for the next page. The last page has no header.

  1. curl -si -H "Authorization: Bearer $ADA" "http://127.0.0.1:8080/api/delegations?limit=2" returns two delegations and an x-next-cursor: MDky... header.
  2. Repeat with &cursor=MDky.... You get the next two, and a new cursor.
  3. Stop when a page comes back without the header.

A cursor stays valid when items are added or removed in between, with no repeats. Paged lists: /threads, /inbox, /approvals, /tasks, /runs, /traces, /knowledge, /decisions, /changes, /delegations, /views, /modules, /catalog, /connections and the gateway's action lists. /traces has its own limit (default 50, at most 200).

GET /events is not paged this way. It takes since (an event number) and limit, and without either returns the newest 200 events with last_seq, the newest event number in the log. The workspace polls /events?since=<last_seq> every two seconds.

Reads of a collection kept in another system can stop early (at 5,000 rows or after 60 seconds). The answer then carries x-ok-partial: true and names the vendor's adapter in x-ok-partial-from. GET /traces also sends x-ok-partial: true when it cut the archived runs short. The demo has no collection kept in another system, so this was not tried there. See Connections.

The live event stream#

GET /api/events/stream sends new events as server-sent events as they are written, only those you may see. Each message has the event number as id, the event type as event, and the event as JSON in data:

id: 422
event: knowledge_proposed
data: {"seq":422,"actor":"maya","event":{"type":"knowledge_proposed",...}}
  • It starts at the newest event. Add since=<n> to start after event n.
  • It takes the same filters as GET /events: type (comma-separated), run, thread, task, actor.
  • A browser's EventSource cannot set headers, so this route alone also accepts the token as ?token=. On any other route a token in the address is ignored and the request gets 401.

To watch Maya's new knowledge entries: curl -N "http://127.0.0.1:8080/api/events/stream?token=$MAYA&type=knowledge_proposed", then add an entry as Maya. It arrives within a second.

When a connection drops, reconnect with since= set to the last id you received. The Last-Event-ID header a browser sends on reconnect is ignored, so events written while you were away are missed unless you pass since.

Route map#

All paths below are under /api. Each area's page has the bodies, answers and error codes in its For developers section.

Area Routes Page
You and the company GET /me, GET/PUT /company, GET /health Company setup, Workspace tour
People and agents GET/POST /actors, GET /actors/:id, POST /actors/:id/offboard, POST /actors/:id/trust, GET /directory/find, GET /directory/cards Company setup, Agents
Blueprints GET/POST /blueprints Company setup
Tokens GET/POST /tokens, DELETE /tokens/:actor, DELETE/PATCH /tokens/id/:id Company setup
Asking and work GET /me/agents, POST /ask, GET/POST /tasks, GET /tasks/:id, POST /tasks/:id/assign, /run, /complete, /refs, GET /runs, GET /runs/:id, POST /runs/:id/answer, GET /traces Asking agents
Threads GET/POST /threads, GET /threads/:id, POST /threads/:id/messages, /ask, /artifacts, /decide, /close, /participants, DELETE /threads/:id/participants/:actor, GET /thread-templates, PUT /artifacts/:id Threads
Inbox and approvals GET /inbox, POST /inbox/:id/read, GET /approvals, GET/POST /approvals/:id, GET /approvals/:id/explain Inbox and approvals
Agents and playbooks GET /library/agents, POST /library/agents/:id/fork, GET /agents/:id/playbook, POST /agents/:id/playbook/propose, /convert, GET /agents/:id/scorecard Agents
Changes GET/POST /changes, POST /changes/:id/review, /evals, /canary, /promote, /reject, GET /changes/:id/diff Changes and evals
The brain /brain/collections... (query, aggregate, records, history, rollback, flag, semantics), GET /brain/catalog, GET /brain/flags, GET /refs/:collection/:id, GET /search, /schema/proposals..., POST /schema/steward, GET /views, GET /views/:id, POST /views/preview The brain
Knowledge and decisions GET/POST /knowledge, GET /knowledge/quarantine, POST /knowledge/:id/accept, /reject, GET /decisions Knowledge and decisions
Context capture /context/sources..., /context/optins..., /context/identities/:actor, POST /context/forget, GET /context/inbox, GET /context/functions, /context/flags..., GET /context/items/:id/links Context capture
Delegations GET/POST /delegations, DELETE /delegations/:id Delegations
Policy GET /policy, GET /policy/export, POST /policy/check, /diff, /proposals, GET /policy/applied, /drift, PUT /policy/source, POST /kill Policy
The gate POST /policy/explain, GET /approvals/:id/explain How the gate decides
Events and audit GET /events, GET /events/stream, GET /audit/head, GET /audit/checkpoints, POST /audit/verify Events and audit
Models GET /llm/models Models
Modules GET /catalog, POST /catalog/uploads, /drafts, DELETE /catalog/:id, /catalog/proposals/:id/..., GET /modules, /modules/:pack... (defaults, enable, pause, resume, reconcile, adopt, webhook secrets), POST /tick, POST /webhooks/:source Modules
Connections GET/POST /connections, PUT/DELETE /connections/:id, POST /connections/:id/test, GET /adapters, POST /webhooks/connections/:id Connections
Packs and skills GET/POST /packs, GET /skills, GET /tools, GET /schemas Packs and skills
Outside agents /gateway/agents... (admin), POST/GET /actions, GET /actions/:id, POST /actions/:id/cancel, /gateway/work-sessions..., GET /gateway/tools, and POST /mcp at the root Outside agents
Several organizations x-ok-tenant header or tenant subdomain; operator API under /operator/... on the operator listener Hosting several organizations

POST /tick is for admins only; anyone else gets 403 "policy denied tick: admins only". POST /webhooks/:source takes a signature instead of a token (see Self-hosting); without a secret configured for the source it answers 401 "no webhook secret is configured for this source".

Known problems#

Found while writing this page; reported for fixing.

  • Commands write over a running server, silently. Any state-file command run while the server is up, even a read such as ok log, writes the file back, and nothing stops or warns you. A change it makes (a kill switch, a token) is reported as done and logged, but the server never applies it and loses it at its next write. The log keeps the event anyway.
  • A forgotten --state makes a new company. In a folder without .orchkernel/state.db, a command quietly creates an empty one.
  • An unknown --as is not always caught. ok inbox, ok tasks and ok threads print nothing and exit 0, and an empty inbox or thread list prints nothing either, so the two look alike. ok log prints the whole log for an actor that does not exist.
  • Some framework errors are not JSON. Malformed JSON, a missing field, a missing content-type and a wrong method answer in plain text or empty, not with error and message.
  • Last-Event-ID is ignored on the event stream; pass since when you reconnect.
  • Token kind has two shapes. POST /tokens answers "kind": "api"; GET /tokens lists "kind": {"type": "api"}.
  • Help text. Many subcommands (ok runs, ok actor list, every ok brain subcommand, ok directory, ok schema, ok changes) show no description in --help, and some help lines cite internal design sections such as "(T§5)", "(SC§5.7)" or "(PC§7)".
  • A demo that cannot start still prints tokens. When the port is taken, ok demo serve prints the tokens and "OrchKernel serving on ..." before it fails with "Address already in use", and the tokens it printed do not work against whatever holds the port.

Not possible yet#

  • Running the state-file commands against a server: only ok policy, ok tenant and ok operator take --server.
  • Issuing a scoped token from the workspace.
  • Resuming the event stream from Last-Event-ID.
  • A published OpenAPI description of the API. GET /api/schemas describes definitions (agents, skills, packs), not routes.