mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
334 lines
12 KiB
Markdown
334 lines
12 KiB
Markdown
# Chats API
|
|
|
|
> [!NOTE]
|
|
> The Chats API is experimental and gated behind the `agents` experiment flag.
|
|
> Endpoints live under `/api/experimental/chats` and may change without notice.
|
|
|
|
The Chats 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 '{
|
|
"organization_id": "<your-org-id>",
|
|
"content": [
|
|
{"type": "text", "text": "hello world"}
|
|
]
|
|
}'
|
|
```
|
|
|
|
The response is the newly created `Chat` object:
|
|
|
|
```json
|
|
{
|
|
"id": "a1b2c3d4-...",
|
|
"organization_id": "...",
|
|
"owner_id": "...",
|
|
"workspace_id": null,
|
|
"build_id": null,
|
|
"agent_id": null,
|
|
"parent_chat_id": null,
|
|
"root_chat_id": null,
|
|
"last_model_config_id": "...",
|
|
"title": "hello world",
|
|
"status": "waiting",
|
|
"last_error": null,
|
|
"diff_status": null,
|
|
"created_at": "2025-07-17T00:00:00Z",
|
|
"updated_at": "2025-07-17T00:00:00Z",
|
|
"archived": false,
|
|
"pin_order": 0,
|
|
"mcp_server_ids": [],
|
|
"labels": {},
|
|
"has_unread": false,
|
|
"client_type": "api"
|
|
}
|
|
```
|
|
|
|
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. |
|
|
| `organization_id` | `uuid` | yes | The organization this chat belongs to. |
|
|
| `workspace_id` | `uuid` | no | Pin the chat to a specific workspace. |
|
|
| `model_config_id` | `uuid` | no | Override the default model configuration. |
|
|
| `mcp_server_ids` | `uuid[]` | no | Attach MCP servers to this chat. |
|
|
| `labels` | `map[string]string` | no | Key-value labels for the chat (max 50). |
|
|
| `client_type` | `string` | no | `"ui"` or `"api"`. Defaults to `"api"`. |
|
|
|
|
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. |
|
|
| `mcp_server_ids` | `uuid[]` | no | Override MCP servers 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.
|
|
|
|
### Edit a message
|
|
|
|
`PATCH /api/experimental/chats/{chat}/messages/{message}`
|
|
|
|
Edits a previously sent user message. The agent re-processes from the
|
|
edited message onward, truncating any messages that followed it.
|
|
|
|
| Field | Type | Required | Description |
|
|
|-----------|-------------------|----------|----------------------------------|
|
|
| `content` | `ChatInputPart[]` | yes | The replacement message content. |
|
|
|
|
The response is an `EditChatMessageResponse` with the edited `message`
|
|
and an optional `warnings` array. When file references in the edited
|
|
content cannot be linked (e.g. the per-chat file cap is reached), the
|
|
edit still succeeds and the `warnings` array describes which files
|
|
were not linked.
|
|
|
|
### 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.
|
|
|
|
Connecting to the stream also updates the caller's read cursor for
|
|
unread tracking. On disconnect the cursor is advanced to the latest
|
|
message.
|
|
|
|
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. |
|
|
|
|
### Watch all chats
|
|
|
|
`GET /api/experimental/chats/watch`
|
|
|
|
Opens a **one-way WebSocket** that pushes events for all chats owned by
|
|
the authenticated user. Use this to drive a sidebar or notification
|
|
indicator without polling.
|
|
|
|
Each event is a JSON object with `kind` and `chat` fields:
|
|
|
|
| Kind | Description |
|
|
|----------------------|----------------------------------|
|
|
| `created` | A new chat was created. |
|
|
| `status_change` | A chat's status changed. |
|
|
| `title_change` | A chat's title was updated. |
|
|
| `diff_status_change` | A chat's diff/PR status changed. |
|
|
| `deleted` | A chat was deleted. |
|
|
|
|
### List chats
|
|
|
|
`GET /api/experimental/chats`
|
|
|
|
Returns all chats owned by the authenticated user. The `files` field is
|
|
populated on `POST /chats` and `GET /chats/{id}`. Other endpoints that
|
|
return a `Chat` object omit it.
|
|
|
|
| Query parameter | Type | Required | Description |
|
|
|-----------------|----------|----------|------------------------------------------------------------------|
|
|
| `q` | `string` | no | Search query string. |
|
|
| `label` | `string` | no | Filter by label as `key:value`. Repeat for multiple (AND logic). |
|
|
|
|
### Get a chat
|
|
|
|
`GET /api/experimental/chats/{chat}`
|
|
|
|
Returns the `Chat` object (metadata only, no messages). The response
|
|
includes a `files` field (`ChatFileMetadata[]`) containing metadata for
|
|
files that have been successfully linked to the chat. File linking is
|
|
best-effort; if linking fails, the file remains in message content but
|
|
will be absent from this field.
|
|
|
|
When file linking is skipped (e.g. the per-chat file cap is reached),
|
|
`POST /chats` includes a `warnings` array on the `Chat` response and
|
|
`POST /chats/{chat}/messages` includes a `warnings` array on the
|
|
`CreateChatMessageResponse`. The `warnings` field is `omitempty` and
|
|
absent when all files are linked successfully.
|
|
|
|
### 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`.
|
|
|
|
### Update a chat
|
|
|
|
`PATCH /api/experimental/chats/{chat}`
|
|
|
|
Updates chat metadata. All fields are optional; omitted fields are left
|
|
unchanged.
|
|
|
|
| Field | Type | Description |
|
|
|-------------|---------------------|-------------------------------------------------------------------------------------|
|
|
| `title` | `string` | Set a new title. |
|
|
| `archived` | `bool` | `true` to archive, `false` to unarchive. Archiving clears `pin_order`. |
|
|
| `pin_order` | `int32` | `0` to unpin; `>0` on an unpinned chat to pin it; `>0` on a pinned chat to reorder. |
|
|
| `labels` | `map[string]string` | Replace all labels. Use `null`/omit to leave unchanged, `{}` to clear. |
|
|
|
|
**Response**: `204 No Content`.
|
|
|
|
### Regenerate title
|
|
|
|
`POST /api/experimental/chats/{chat}/title/regenerate`
|
|
|
|
Regenerates the chat title using conversation context. Returns the
|
|
updated `Chat` object.
|
|
|
|
### Interrupt
|
|
|
|
`POST /api/experimental/chats/{chat}/interrupt`
|
|
|
|
Stops the agent's current processing loop and returns the chat to
|
|
`waiting` status.
|
|
|
|
### Manage queued messages
|
|
|
|
When a message is queued because the agent is busy, you can manage the
|
|
queue:
|
|
|
|
`DELETE /api/experimental/chats/{chat}/queue/{queuedMessage}`
|
|
|
|
Removes a queued message before it is processed.
|
|
|
|
`POST /api/experimental/chats/{chat}/queue/{queuedMessage}/promote`
|
|
|
|
Promotes a queued message to be processed next.
|
|
|
|
### Get diff contents
|
|
|
|
`GET /api/experimental/chats/{chat}/diff`
|
|
|
|
Returns the current diff/PR status for a chat, including additions,
|
|
deletions, changed files, and pull request metadata when available.
|
|
|
|
## 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`.
|
|
|
|
Files referenced in messages are automatically linked to the chat and
|
|
appear in the `files` field on subsequent
|
|
`GET /api/experimental/chats/{chat}` responses.
|
|
|
|
## Chat statuses
|
|
|
|
| Status | Meaning |
|
|
|-------------------|------------------------------------------------------------------------------|
|
|
| `waiting` | No pending work (newly created, finished, or interrupted). |
|
|
| `pending` | Queued for processing. |
|
|
| `running` | Agent is actively working. |
|
|
| `error` | Agent encountered an error. |
|
|
| `requires_action` | Agent invoked a client-provided tool and needs the result before continuing. |
|