.agents/skills/opencode-qa/references/server-api.md
Run the server with a fixed port and host:
opencode serve --port 4096 --hostname 127.0.0.1
Output:
opencode server listening on http://127.0.0.1:4096
Port 0 means the server will pick 4096, then fall back to a free port if that one is taken.
A bundled isolated smoke test is available at scripts/server-smoke.sh. It spawns an isolated server, checks /global/health, checks that /doc returns at least 100 paths, and confirms that no-auth requests get 401, then tears the server down.
Set the environment variable OPENCODE_SERVER_PASSWORD to require authentication. If it is unset, the server runs UNSECURED and prints a warning.
The username defaults to opencode. Override it with OPENCODE_SERVER_USERNAME.
Two ways to authenticate:
-u opencode:$PASS?auth_token=<base64(user:pass)>Unauthenticated requests to protected routes return HTTP 401. This was verified.
Most instance routes need the target project directory. Pass it as either:
?directory=$PWDx-opencode-directory: $PWDAliases also work: x-opencode-workspace header or ?workspace= query parameter.
The server resolves an instance per request, so a single serve process can handle many projects.
The /doc endpoint returns the full OpenAPI spec. To list all documented paths:
curl -s -u opencode:$PASS http://127.0.0.1:4096/doc | jq '.paths | keys'
On v1.15.13 this returned 113 paths. This is the source of truth for exact request and response schemas.
curl -s -u opencode:$PASS http://127.0.0.1:4096/global/health | jq .
# {"healthy":true,"version":"1.15.13"}
curl -s -u opencode:$PASS http://127.0.0.1:4096/doc | jq '.paths|length'
# 113
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:4096/session?directory=$PWD
# 401 (no credentials)
curl -s -u opencode:$PASS "http://127.0.0.1:4096/session?directory=$PWD" | jq 'length'
This mirrors the structure returned by /doc. Each entry is grouped as method path - purpose.
GET /global/health - health checkGET /global/event - server-wide SSE event streamGET /global/config - read global configurationPATCH /global/config - update global configurationPOST /global/dispose - dispose the server instanceGET /doc - OpenAPI specificationGET /session - list sessionsGET /session/status - session status overviewGET /session/:id - get session by IDGET /session/:id/children - list child sessionsGET /session/:id/todo - get session todo itemsGET /session/:id/diff - get session diffGET /session/:id/message - list session messagesGET /session/:id/message/:messageID - get a specific messagePOST /session - create a new sessionDELETE /session/:id - delete a sessionPATCH /session/:id - update a sessionPOST /session/:id/fork - fork a sessionPOST /session/:id/abort - abort a sessionPOST /session/:id/init - initialize a sessionPOST /session/:id/share - share a sessionPOST /session/:id/summarize - summarize a sessionPOST /session/:id/revert - revert a sessionPOST /session/:id/unrevert - unrevert a sessionDELETE /session/:id/share - unshare a sessionPOST /session/:id/message - send a prompt; streams JSONPOST /session/:id/prompt_async - fire-and-forget prompt; returns 204POST /session/:id/command - execute a command in a sessionPOST /session/:id/shell - run a shell command in a sessionGET /find - text search via ripgrepGET /find/file - file searchGET /find/symbol - symbol searchGET /file - file metadataGET /file/content - file contentsGET /file/status - file statusGET /path - path resolutionGET /vcs - version control infoGET /vcs/status - VCS statusGET /vcs/diff - VCS diffGET /command - available commandsGET /agent - available agentsGET /skill - available skillsGET /lsp - LSP infoGET /formatter - formatter infoGET /permission - list pending permission requestsPOST /permission/:requestID/reply - reply to a permission requestGET /question - list pending questionsPOST /question/:requestID/reply - reply to a questionPOST /question/:requestID/reject - reject a questionThese endpoints drive a running TUI over HTTP.
POST /tui/append-prompt - append text to the TUI promptPOST /tui/submit-prompt - submit the current TUI promptPOST /tui/execute-command - execute a TUI commandPOST /tui/show-toast - show a toast in the TUIGET /tui/control/next - get the next TUI control eventPOST /tui/control/response - respond to a TUI control eventGET /pty - list PTY sessionsPOST /pty - create a PTY sessionGET /pty/:id - get PTY session infoDELETE /pty/:id - delete a PTY sessionPOST /pty/:id/connect-token - generate a PTY connect tokenGET /pty/:id/connect - WebSocket connection to the PTYGET /event - instance-level SSE event streamGET /api/session - list sessions (v2)POST /api/session/:id/prompt - prompt a session (v2)POST /api/session/:id/compact - compact a session (v2)POST /api/session/:id/wait - wait for a session (v2)GET /api/session/:id/context - get session context (v2)GET /api/session/:id/message - get session messages (v2)GET /api/model - list models (v2)GET /api/provider - list providers (v2)Use prompt_async so the event stream is not blocked.
curl -X POST -u opencode:$PASS -H 'Content-Type: application/json' \
-d '{"parts":[{"type":"text","text":"hello"}]}' \
"http://127.0.0.1:4096/session/<ses_id>/prompt_async?directory=$PWD"
This returns HTTP 204. Watching events is covered in references/events-hooks.md.
Schemas are authoritative in GET /doc; for the event stream see references/events-hooks.md.