# 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

Local/dev uses opencode's default port `4096`:

```bash
opencode serve \
  --hostname 127.0.0.1 \
  --port 4096
```

Public production uses port `4242`; see [`public-ports.md`](public-ports.md):

```bash
opencode serve \
  --hostname 0.0.0.0 \
  --port 4242
```

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 <base64(username:password)>` 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.

Optional query parameters used by `ai`:
- `directory` — scope results to an opencode project directory.

**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" }
  ],
  "agent": "atm10-expert",
  "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" }
}
```

`agent` and `model` are optional — omit them to use the server's configured defaults.

**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 and starts generation in the background. Include `messageID` in the request body so the submitted user message can be matched to the later assistant response. Opencode validates caller-provided message IDs; use IDs starting with `msg`.

**Request body:**
```json
{
  "messageID": "msg_xyz",
  "parts": [
    { "type": "text", "text": "your prompt here" }
  ],
  "agent": "atm10-expert",
  "model": { "providerID": "anthropic", "modelID": "claude-opus-4-7" }
}
```

`agent` and `model` are optional. Omit `model` to use the server/session default model, or include it to force a specific provider/model for this prompt.

`ai` uses this endpoint by default 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 message; `ai` polls `GET /session/:id/message` and reads the completed assistant message after it. If `opencc.agent` or `--agent <name>` is set, `ai` includes it as `agent` in the request body. If generation fails in the background, opencode records the failure on the assistant message or session event stream; `ai` surfaces assistant message errors while polling.

---

### `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.
