diff --git a/docs/ai-coder/agents/chat-api.md b/docs/ai-coder/agents/chat-api.md new file mode 100644 index 0000000000..4c64e1f12e --- /dev/null +++ b/docs/ai-coder/agents/chat-api.md @@ -0,0 +1,219 @@ +# Chat API + +> [!NOTE] +> The Chat API is experimental and gated behind the `agents` experiment flag. +> Endpoints live under `/api/experimental/chats` and may change without notice. + +The Chat API lets you create and interact with Coder Agents +programmatically. You can start a chat, send follow-up messages, and stream +the agent's response — all without using the Coder dashboard. + +## Authentication + +All endpoints require a valid session token: + +```sh +curl -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \ + https://coder.example.com/api/experimental/chats +``` + +## Quick start + +Create a chat with a single text prompt: + +```sh +curl -X POST https://coder.example.com/api/experimental/chats \ + -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "content": [ + {"type": "text", "text": "hello world"} + ] + }' +``` + +The response is the newly created `Chat` object: + +```json +{ + "id": "a1b2c3d4-...", + "owner_id": "...", + "workspace_id": null, + "last_model_config_id": "...", + "title": "hello world", + "status": "waiting", + "last_error": null, + "created_at": "2025-07-17T00:00:00Z", + "updated_at": "2025-07-17T00:00:00Z", + "archived": false +} +``` + +The agent begins processing the prompt asynchronously. Use the +[stream endpoint](#stream-updates) to follow its progress. + +## Core workflow + +A typical integration follows three steps: + +1. **Create a chat** — `POST /api/experimental/chats` with your prompt. +2. **Stream updates** — Open a WebSocket to + `GET /api/experimental/chats/{chat}/stream` to receive real-time events + as the agent works. +3. **Send follow-ups** — `POST /api/experimental/chats/{chat}/messages` to + add messages to the conversation. Messages are queued if the agent is + busy. + +## Endpoints + +### Create a chat + +`POST /api/experimental/chats` + +| Field | Type | Required | Description | +|-------------------|-------------------|----------|-------------------------------------------------| +| `content` | `ChatInputPart[]` | yes | The user's prompt as one or more content parts. | +| `workspace_id` | `uuid` | no | Pin the chat to a specific workspace. | +| `model_config_id` | `uuid` | no | Override the default model configuration. | + +Each `ChatInputPart` has a `type` field. The simplest form is a text part: + +```json +{"type": "text", "text": "Fix the failing tests in the auth service"} +``` + +Other part types include `file` (an uploaded image referenced by its +`file_id`) and `file-reference` (a pointer to a file with optional line +range). + +**Response**: `201 Created` with a `Chat` object. + +### Send a message + +`POST /api/experimental/chats/{chat}/messages` + +| Field | Type | Required | Description | +|-------------------|-------------------|----------|-----------------------------------| +| `content` | `ChatInputPart[]` | yes | The follow-up message content. | +| `model_config_id` | `uuid` | no | Override the model for this turn. | + +If the agent is currently processing, the message is queued automatically. +The response indicates whether the message was delivered immediately or +queued: + +```json +{ + "queued": false, + "message": { "id": 42, "chat_id": "...", "role": "user", "created_at": "...", "content": [...] } +} +``` + +When `queued` is `true`, `message` is absent and `queued_message` is +returned instead. + +### Stream updates + +`GET /api/experimental/chats/{chat}/stream` + +Opens a **one-way WebSocket** connection. The server sends events; clients +must not write to the socket (doing so closes the connection). + +| Query parameter | Type | Required | Description | +|-----------------|---------|----------|-------------------------------------------| +| `after_id` | `int64` | no | Only return events after this message ID. | + +Each WebSocket message is a JSON envelope with an outer `type` +(`"ping"`, `"data"`, or `"error"`) and an optional `data` field. For +`"data"` envelopes the payload is a **JSON array** of event objects: + +```json +{ + "type": "data", + "data": [ + {"type": "status", "chat_id": "...", "status": {"status": "running"}}, + {"type": "message_part", "chat_id": "...", "message_part": {"...":"..."}} + ] +} +``` + +Ignore `"ping"` envelopes (keepalives sent every ~15 s). On first +connect the server sends an initial snapshot of the chat state before +switching to live events. Use `after_id` when reconnecting to skip +messages the client already has. + +Event types inside each batch: + +| Type | Description | +|----------------|--------------------------------------------------------------| +| `message_part` | A chunk of the agent's response (text, tool call, etc.). | +| `message` | A complete message has been persisted. | +| `status` | The chat status changed (e.g. `running`, `waiting`). | +| `error` | An error occurred during processing. | +| `retry` | The server is retrying a failed LLM call (includes backoff). | +| `queue_update` | The queued message list changed. | + +### List chats + +`GET /api/experimental/chats` + +Returns all chats owned by the authenticated user. + +### Get a chat + +`GET /api/experimental/chats/{chat}` + +Returns the `Chat` object (metadata only, no messages). + +### Get chat messages + +`GET /api/experimental/chats/{chat}/messages` + +Returns the messages and queued messages for a chat. + +### List models + +`GET /api/experimental/chats/models` + +Returns available models. Use this to discover valid values for +`model_config_id`. + +### Archive / unarchive + +`POST /api/experimental/chats/{chat}/archive` +`POST /api/experimental/chats/{chat}/unarchive` + +Archive hides a chat from the default list without deleting it. + +### Interrupt + +`POST /api/experimental/chats/{chat}/interrupt` + +Stops the agent's current processing loop and returns the chat to +`waiting` status. + +## File uploads + +Attach images to a chat by uploading them first: + +```sh +curl -X POST "https://coder.example.com/api/experimental/chats/files?organization=$ORG_ID" \ + -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \ + -H "Content-Type: image/png" \ + --data-binary @screenshot.png +``` + +The response contains an `id` you can reference as `file_id` in a +`ChatInputPart` with `"type": "file"`. To retrieve a previously uploaded +file, use `GET /api/experimental/chats/files/{file}`. + +Supported formats: PNG, JPEG, GIF, WebP (up to 10 MB). The server +validates actual file content regardless of the declared `Content-Type`. + +## Chat statuses + +| Status | Meaning | +|-----------|--------------------------------------------------------------| +| `waiting` | Idle — newly created, finished successfully, or interrupted. | +| `pending` | Queued for processing. | +| `running` | Agent is actively working. | +| `error` | Agent encountered an error. | diff --git a/docs/manifest.json b/docs/manifest.json index 5471a5f37d..4d39e0b11f 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1211,6 +1211,12 @@ "path": "./ai-coder/agents/platform-controls.md", "state": ["early access"] }, + { + "title": "Chat API", + "description": "Programmatic access to Coder Agents via the experimental Chat API", + "path": "./ai-coder/agents/chat-api.md", + "state": ["early access"] + }, { "title": "Early Access", "description": "About the Coder Agents Early Access program",