# opencode serve — HTTP API reference Minimal reference for the endpoints used by `libai.lua`. Full spec served at `GET /doc` when the server is running. ## Running the server ```bash opencode serve \ --hostname 127.0.0.1 \ --port 4096 ``` With Basic Auth (recommended): ```bash OPENCODE_SERVER_PASSWORD=secret opencode serve \ --hostname 127.0.0.1 \ --port 4096 ``` Default username is `opencode`. Override with `OPENCODE_SERVER_USERNAME`. ## Authentication All requests must include `Authorization: Basic ` when the server was started with a password. No auth header is needed if no password is set. ``` Authorization: Basic base64("opencode:secret") ``` ## Endpoints ### `GET /global/health` Health check. Returns `200` when the server is up. --- ### `GET /session` List all sessions for the current project. **Response** `200`: ```json [ { "id": "ses_abc123", "title": "my session", "time": { "created": 1234567890, "updated": 1234567890 } } ] ``` --- ### `POST /session` Create a new session. **Request body:** ```json { "title": "optional title", "parentID": "optional" } ``` **Response** `200`: ```json { "id": "ses_abc123", "title": "my session" } ``` --- ### `POST /session/:id/message` Send a message and wait for the AI reply (blocking). Returns when the assistant has finished responding. **Request body:** ```json { "parts": [ { "type": "text", "text": "your prompt here" } ], "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" } } ``` `model` is optional — omit to use the server's configured default. **Response** `200`: ```json { "info": { "id": "msg_xyz", "sessionID": "ses_abc123", "role": "assistant" }, "parts": [ { "type": "text", "text": "the reply" } ] } ``` Parts can include non-text types (`tool-call`, `step-start`, etc.) — collect all `type == "text"` entries to reconstruct the reply. **Errors:** - `401` — wrong credentials - `404` — session not found (may have been deleted or server restarted) - `504` — AI took too long --- ### `GET /session/:id/message/:messageID` Get a message by ID. Opencode validates caller-provided message IDs; use IDs starting with `msg`. **Response** `200`: ```json { "info": { "id": "msg_xyz", "sessionID": "ses_abc123", "role": "assistant", "time": { "completed": 1234567890 } }, "parts": [ { "type": "text", "text": "the reply" } ] } ``` --- ### `DELETE /session/:id` Delete a session. --- ### `POST /session/:id/abort` Abort a running generation. --- ### `POST /session/:id/prompt_async` Fire-and-forget variant. Returns `204` immediately. Include `messageID` in the request body to make the assistant response addressable by `GET /session/:id/message/:messageID`. Opencode validates caller-provided message IDs; use IDs starting with `msg`. **Request body:** ```json { "messageID": "msg_xyz", "parts": [ { "type": "text", "text": "your prompt here" } ], "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" } } ``` Unlike `/message`, `model` is **not** optional in practice — omitting it causes the request to be accepted (`204`) without triggering generation, so the assistant message never appears. `ai` only uses this async endpoint when `opencc.provider_id` and `opencc.model_id` are configured; otherwise it falls back to blocking `POST /session/:id/message`. When async mode is available, `ai` uses this endpoint to avoid `504` failures from the blocking `/message` endpoint when the LLM takes longer than one HTTP request timeout. The submitted `messageID` identifies the user or assistant message depending on the opencode response shape; `ai` polls `GET /session/:id/message` and reads the completed assistant message. --- ### `GET /global/event` (SSE) Server-Sent Events stream. Delivers all server bus events in real time. Useful for async workflows or watching a session from outside the TUI. --- ## opencode attach `opencode attach` opens the TUI and connects it to an already-running `opencode serve` instance. Both the TUI and any HTTP clients operate on the same session state. ```bash opencode attach http://127.0.0.1:4096 opencode attach http://127.0.0.1:4096 --session ses_abc123 ``` To send messages to the session currently open in the TUI, set `opencc.session_id` in CC to the session ID shown in the TUI, then run `ai`: ```sh set opencc.session_id ses_abc123 ``` ## TUI control When a TUI is attached, these endpoints drive that TUI programmatically. They operate on the attached TUI prompt/dialog state, not on a specific `/session/:id`. | Method | Path | Effect | Body | |---|---|---|---| | `POST` | `/tui/append-prompt` | Append text to the prompt | `{ "text": "..." }` | | `POST` | `/tui/submit-prompt` | Submit the current prompt | none | | `POST` | `/tui/clear-prompt` | Clear the prompt | none | | `POST` | `/tui/open-help` | Open the help dialog | none | | `POST` | `/tui/open-sessions` | Open the session selector | none | | `POST` | `/tui/open-themes` | Open the theme selector | none | | `POST` | `/tui/open-models` | Open the model selector | none | | `POST` | `/tui/execute-command` | Execute a TUI command | `{ "command": "prompt.submit" }` | | `POST` | `/tui/show-toast` | Show a notification | `{ "message": "...", "variant": "info" }` | | `GET` | `/tui/control/next` | Wait for the next TUI control request | none | | `POST` | `/tui/control/response` | Respond to a TUI control request | response body | `/tui/show-toast` also accepts optional `title` and `duration` fields. `variant` is one of `info`, `success`, `warning`, or `error`. Example: prefill and submit the attached TUI prompt: ```bash curl -X POST http://127.0.0.1:4096/tui/append-prompt \ -H 'Content-Type: application/json' \ -d '{"text":"reply with exactly: pong"}' curl -X POST http://127.0.0.1:4096/tui/submit-prompt ``` `POST /tui/publish` also exists as a lower-level endpoint for publishing TUI events such as `tui.prompt.append`, `tui.command.execute`, and `tui.toast.show`. Prefer the specific endpoints above unless you need that raw event surface.