mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
docs: generate Chats API docs from swagger annotations (#24830)
This commit is contained in:
@@ -110,9 +110,6 @@ app, err := api.Database.GetOAuth2ProviderAppByClientID(ctx, clientID)
|
||||
- For experimental or unstable API paths, skip public doc generation with
|
||||
`// @x-apidocgen {"skip": true}` after the `@Router` annotation. This
|
||||
keeps them out of the published API reference until they stabilize.
|
||||
- Experimental chat endpoints in `coderd/exp_chats.go` omit swagger
|
||||
annotations entirely. Do not add `@Summary`, `@Router`, or other
|
||||
swagger comments to handlers in that file.
|
||||
|
||||
### Database Query Naming
|
||||
|
||||
|
||||
Generated
+1805
File diff suppressed because it is too large
Load Diff
Generated
+1698
File diff suppressed because it is too large
Load Diff
@@ -161,6 +161,15 @@ func publishChatConfigEvent(logger slog.Logger, ps dbpubsub.Pubsub, kind pubsub.
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Watch chat events for a user via WebSockets
|
||||
// @ID watch-chat-events-for-a-user-via-websockets
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Success 200 {object} codersdk.ChatWatchEvent
|
||||
// @Router /experimental/chats/watch [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) watchChats(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -297,6 +306,17 @@ func (api *API) chatsByWorkspace(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary List chats
|
||||
// @ID list-chats
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param q query string false "Search query"
|
||||
// @Param label query string false "Filter by label as key:value. Repeat for multiple (AND logic)."
|
||||
// @Success 200 {array} codersdk.Chat
|
||||
// @Router /experimental/chats [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) listChats(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -903,6 +923,17 @@ func (api *API) validateUserChatModelConfigAvailable(
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Create chat
|
||||
// @ID create-chat
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body codersdk.CreateChatRequest true "Create chat request"
|
||||
// @Success 201 {object} codersdk.Chat
|
||||
// @Router /experimental/chats [post]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) postChats(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -1184,6 +1215,15 @@ func (api *API) postChats(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary List chat models
|
||||
// @ID list-chat-models
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Success 200 {object} codersdk.ChatModelsResponse
|
||||
// @Router /experimental/chats/models [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) listChatModels(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -1873,6 +1913,16 @@ func (api *API) deleteChatUsageLimitGroupOverride(rw http.ResponseWriter, r *htt
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Get chat by ID
|
||||
// @ID get-chat-by-id
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Chat
|
||||
// @Router /experimental/chats/{chat} [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) getChat(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -1937,6 +1987,19 @@ func (api *API) getChat(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary List chat messages
|
||||
// @ID list-chat-messages
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Param before_id query int false "Return messages with id < before_id"
|
||||
// @Param after_id query int false "Return messages with id > after_id"
|
||||
// @Param limit query int false "Page size, 1 to 200. Defaults to 50."
|
||||
// @Success 200 {object} codersdk.ChatMessagesResponse
|
||||
// @Router /experimental/chats/{chat}/messages [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) getChatMessages(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -2080,6 +2143,16 @@ func (api *API) authorizeChatWorkspaceExec(
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Watch chat workspace git state via WebSockets
|
||||
// @ID watch-chat-workspace-git-state-via-websockets
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.WorkspaceAgentGitServerMessage
|
||||
// @Router /experimental/chats/{chat}/stream/git [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) watchChatGit(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
@@ -2225,6 +2298,17 @@ proxyLoop:
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Connect to chat workspace desktop via WebSockets
|
||||
// @ID connect-to-chat-workspace-desktop-via-websockets
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce application/octet-stream
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 101
|
||||
// @Router /experimental/chats/{chat}/stream/desktop [get]
|
||||
// @Description Raw binary WebSocket stream of the chat workspace desktop.
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) watchChatDesktop(rw http.ResponseWriter, r *http.Request) {
|
||||
var (
|
||||
@@ -2404,6 +2488,17 @@ func (api *API) applyChatTitleUpdate(
|
||||
|
||||
// patchChat updates a chat resource. Supports updating labels,
|
||||
// workspace binding, archiving, pinning, and pinned-chat ordering.
|
||||
//
|
||||
// @Summary Update chat
|
||||
// @ID update-chat
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Accept json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Param request body codersdk.UpdateChatRequest true "Update chat request"
|
||||
// @Success 204
|
||||
// @Router /experimental/chats/{chat} [patch]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) patchChat(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
chat := httpmw.ChatParam(r)
|
||||
@@ -2702,6 +2797,18 @@ func (api *API) writeChildUnarchiveGuard(
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Send chat message
|
||||
// @ID send-chat-message
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Param request body codersdk.CreateChatMessageRequest true "Create chat message request"
|
||||
// @Success 200 {object} codersdk.CreateChatMessageResponse
|
||||
// @Router /experimental/chats/{chat}/messages [post]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -2874,6 +2981,19 @@ func (api *API) postChatMessages(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Edit chat message
|
||||
// @ID edit-chat-message
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Param message path int true "Message ID"
|
||||
// @Param request body codersdk.EditChatMessageRequest true "Edit chat message request"
|
||||
// @Success 200 {object} codersdk.EditChatMessageResponse
|
||||
// @Router /experimental/chats/{chat}/messages/{message} [patch]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) patchChatMessage(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -3102,6 +3222,16 @@ func (api *API) markChatAsRead(ctx context.Context, chatID uuid.UUID) {
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Stream chat events via WebSockets
|
||||
// @ID stream-chat-events-via-websockets
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.ChatStreamEvent
|
||||
// @Router /experimental/chats/{chat}/stream [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) streamChat(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
chat := httpmw.ChatParam(r)
|
||||
@@ -3237,6 +3367,16 @@ func (api *API) streamChat(rw http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Interrupt chat
|
||||
// @ID interrupt-chat
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Produce json
|
||||
// @Success 200 {object} codersdk.Chat
|
||||
// @Router /experimental/chats/{chat}/interrupt [post]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
chat := httpmw.ChatParam(r)
|
||||
@@ -3270,6 +3410,16 @@ func (api *API) interruptChat(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Regenerate chat title
|
||||
// @ID regenerate-chat-title
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.Chat
|
||||
// @Router /experimental/chats/{chat}/title/regenerate [post]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) regenerateChatTitle(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -3356,6 +3506,16 @@ func (api *API) proposeChatTitle(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Get chat diff contents
|
||||
// @ID get-chat-diff-contents
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce json
|
||||
// @Param chat path string true "Chat ID" format(uuid)
|
||||
// @Success 200 {object} codersdk.ChatDiffContents
|
||||
// @Router /experimental/chats/{chat}/diff [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
//
|
||||
//nolint:revive // HTTP handler writes to ResponseWriter.
|
||||
func (api *API) getChatDiffContents(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
@@ -5509,6 +5669,18 @@ func (api *API) deleteUserChatCompactionThreshold(rw http.ResponseWriter, r *htt
|
||||
rw.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Upload chat file
|
||||
// @ID upload-chat-file
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Accept image/png,image/jpeg,image/gif,image/webp,text/plain,text/markdown,text/csv,application/json,application/pdf
|
||||
// @Produce json
|
||||
// @Param organization query string true "Organization ID" format(uuid)
|
||||
// @Success 201 {object} codersdk.UploadChatFileResponse
|
||||
// @Router /experimental/chats/files [post]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) postChatFile(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
apiKey := httpmw.APIKey(r)
|
||||
@@ -5637,6 +5809,17 @@ func (api *API) postChatFile(rw http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// EXPERIMENTAL: this endpoint is experimental and is subject to change.
|
||||
//
|
||||
// @Summary Get chat file
|
||||
// @ID get-chat-file
|
||||
// @Security CoderSessionToken
|
||||
// @Tags Chats
|
||||
// @Produce image/png,image/jpeg,image/gif,image/webp,text/plain,text/markdown,text/csv,application/json,application/pdf
|
||||
// @Param file path string true "File ID" format(uuid)
|
||||
// @Success 200
|
||||
// @Router /experimental/chats/files/{file} [get]
|
||||
// @Description Experimental: this endpoint is subject to change.
|
||||
func (api *API) chatFileByID(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ type Chat struct {
|
||||
HasUnread bool `json:"has_unread"`
|
||||
// LastInjectedContext holds the most recently persisted
|
||||
// injected context parts (AGENTS.md files and skills). It
|
||||
// is updated only when context changes — first workspace
|
||||
// is updated only when context changes, on first workspace
|
||||
// attach or agent change.
|
||||
LastInjectedContext []ChatMessagePart `json:"last_injected_context,omitempty"`
|
||||
Warnings []string `json:"warnings,omitempty"`
|
||||
|
||||
@@ -1,406 +0,0 @@
|
||||
# Chats API
|
||||
|
||||
> [!NOTE]
|
||||
> The Chats API is in beta.
|
||||
> 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",
|
||||
"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"
|
||||
}
|
||||
```
|
||||
|
||||
If a chat later ends in error, the same `Chat` shape includes a structured
|
||||
`last_error` object. For brevity, unchanged nullable IDs are omitted here:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "a1b2c3d4-...",
|
||||
"title": "hello world",
|
||||
"status": "error",
|
||||
"last_error": {
|
||||
"message": "Azure OpenAI is rate limiting requests.",
|
||||
"kind": "rate_limit",
|
||||
"provider": "azure",
|
||||
"retryable": true,
|
||||
"status_code": 429,
|
||||
"detail": "Retry after 30 seconds."
|
||||
},
|
||||
"created_at": "2025-07-17T00:00:00Z",
|
||||
"updated_at": "2025-07-17T00:00:30Z",
|
||||
"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. |
|
||||
| `action_required` | A chat is waiting for a tool result. |
|
||||
| `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 messages for a chat in descending ID order (newest first).
|
||||
|
||||
| Query parameter | Type | Required | Description |
|
||||
|-----------------|---------|----------|---------------------------------------------|
|
||||
| `before_id` | `int64` | no | Only return messages with `id < before_id`. |
|
||||
| `after_id` | `int64` | no | Only return messages with `id > after_id`. |
|
||||
| `limit` | `int32` | no | Page size, 1 to 200. Defaults to 50. |
|
||||
|
||||
Results are returned in descending ID order (newest first), except when
|
||||
only `after_id` is set: that shape is intended for polling and returns
|
||||
ASCENDING ID order so a client can advance its cursor to the largest
|
||||
returned ID without gaps. When both cursors are set they must satisfy
|
||||
`after_id < before_id`; otherwise the server returns `400 Bad Request`.
|
||||
|
||||
`queued_messages` is only populated on the initial load (no
|
||||
cursor). Pass either cursor to page through history or to poll
|
||||
for new messages without receiving the queued snapshot on every
|
||||
request. The `has_more` flag indicates more rows exist beyond
|
||||
this page in the same direction.
|
||||
|
||||
### 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` | Idle. Newly created, finished successfully, 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. |
|
||||
|
||||
## Configuration
|
||||
|
||||
Deployment-wide chat settings are read and written under
|
||||
`/api/experimental/chats/config/*`. Reading config requires authentication; writing requires
|
||||
deployment-admin privileges.
|
||||
|
||||
### Auto-archive window
|
||||
|
||||
Chats whose newest non-deleted message is older than
|
||||
`auto_archive_days` are automatically archived by a background job.
|
||||
Pinned chats and chats belonging to a still-active thread are
|
||||
exempt. `0` disables the feature, which is the default.
|
||||
|
||||
```sh
|
||||
# Read
|
||||
curl -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
|
||||
https://coder.example.com/api/experimental/chats/config/auto-archive-days
|
||||
# { "auto_archive_days": 0 }
|
||||
|
||||
# Update
|
||||
curl -X PUT -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"auto_archive_days": 60}' \
|
||||
https://coder.example.com/api/experimental/chats/config/auto-archive-days
|
||||
```
|
||||
|
||||
Accepted range: `0` to `3650` (~10 years).
|
||||
@@ -236,7 +236,7 @@ multiplier, not a replacement for developer judgment.
|
||||
|
||||
### Use the API for programmatic automation
|
||||
|
||||
The [Chats API](./chats-api.md) enables programmatic access to Coder Agents.
|
||||
The [Chats API](../../reference/api/chats.md) enables programmatic access to Coder Agents.
|
||||
This is useful for building automations such as:
|
||||
|
||||
- Triggering Coder Agents from CI/CD pipelines when builds fail.
|
||||
@@ -270,7 +270,7 @@ narrowly scoped.
|
||||
|
||||
> [!NOTE]
|
||||
> The Chats API is in beta and may change without notice.
|
||||
> See [Chats API](./chats-api.md) for the full endpoint reference.
|
||||
> See [Chats API](../../reference/api/chats.md) for the full endpoint reference.
|
||||
|
||||
### Add workspace context with AGENTS.md
|
||||
|
||||
@@ -322,4 +322,4 @@ Your input directly influences product direction during Beta.
|
||||
- [Template Optimization](./platform-controls/template-optimization.md) —
|
||||
create agent-friendly templates with network boundaries and scoped
|
||||
credentials.
|
||||
- [Chats API](./chats-api.md) — build programmatic integrations.
|
||||
- [Chats API](../../reference/api/chats.md): build programmatic integrations.
|
||||
|
||||
@@ -13,7 +13,7 @@ Below are common scenarios where AI coding agents provide the most impact, along
|
||||
| **Automating actions in the IDE** | Supplement tedious development with agents | Small refactors, generating unit tests, writing inline documentation, code search and navigation | [IDE Agents](./ide-agents.md) in Workspaces |
|
||||
| **Developer-led investigation and setup** | Developers delegate research and initial implementation to AI, then take over in their preferred IDE to complete the work | Bug triage and analysis, exploring technical approaches, understanding legacy code, creating starter implementations | [Coder Agents](./agents/index.md), to a full IDE with [Workspaces](../user-guides/workspace-access/index.md) |
|
||||
| **Prototyping & Business Applications** | User-friendly interface for engineers and non-technical users to build and prototype within new or existing codebases | Creating dashboards, building simple web apps, data analysis workflows, proof-of-concept development | [Coder Agents](./agents/index.md) |
|
||||
| **Full background jobs & long-running agents** | Agents that run independently without user interaction for extended periods of time | Automated code reviews, scheduled data processing, continuous integration tasks, monitoring and alerting | [Coder Agents API](./agents/chats-api.md) |
|
||||
| **Full background jobs & long-running agents** | Agents that run independently without user interaction for extended periods of time | Automated code reviews, scheduled data processing, continuous integration tasks, monitoring and alerting | [Coder Agents API](../reference/api/chats.md) |
|
||||
| **External agents and chat clients** | External AI agents and chat clients that need access to Coder workspaces for development environments and code sandboxing | ChatGPT, Claude Desktop, custom enterprise agents running tests, performing development tasks, code analysis | [MCP Server](./mcp-server.md) |
|
||||
|
||||
## Provide Agents with Proper Context
|
||||
|
||||
+8
-7
@@ -1063,12 +1063,6 @@
|
||||
"path": "./ai-coder/agents/extending-agents.md",
|
||||
"state": ["beta"]
|
||||
},
|
||||
{
|
||||
"title": "Chats API",
|
||||
"description": "Programmatic access to Coder Agents via the Chats API",
|
||||
"path": "./ai-coder/agents/chats-api.md",
|
||||
"state": ["beta"]
|
||||
},
|
||||
{
|
||||
"title": "Tasks to Chats API Migration",
|
||||
"description": "Guide for migrating from the Tasks API to the Chats API",
|
||||
@@ -1300,6 +1294,12 @@
|
||||
"title": "Create a GitHub to Coder Tasks Workflow",
|
||||
"description": "How to setup Coder Tasks to run in GitHub",
|
||||
"path": "./ai-coder/github-to-tasks.md"
|
||||
},
|
||||
{
|
||||
"title": "Tasks to Chats API Migration",
|
||||
"description": "Guide for migrating from the Tasks API to the Chats API",
|
||||
"path": "./ai-coder/agents/tasks-to-chats-migration.md",
|
||||
"state": ["beta"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1502,7 +1502,8 @@
|
||||
},
|
||||
{
|
||||
"title": "Chats",
|
||||
"path": "./reference/api/chats.md"
|
||||
"path": "./reference/api/chats.md",
|
||||
"state": ["early access"]
|
||||
},
|
||||
{
|
||||
"title": "Debug",
|
||||
|
||||
Generated
+2732
File diff suppressed because it is too large
Load Diff
Generated
+2152
File diff suppressed because it is too large
Load Diff
@@ -209,12 +209,25 @@ func writeDocs(sections [][]byte) error {
|
||||
continue
|
||||
}
|
||||
|
||||
// Preserve existing state and description on children, keyed by
|
||||
// title, so that callouts like `state: ["experimental"]` survive
|
||||
// regeneration. Generated routes always overwrite Title and Path.
|
||||
existingByTitle := make(map[string]route, len(child.Children))
|
||||
for _, existing := range child.Children {
|
||||
existingByTitle[existing.Title] = existing
|
||||
}
|
||||
|
||||
var children []route
|
||||
for _, mdf := range mdFiles {
|
||||
docRoute := route{
|
||||
Title: mdf.title,
|
||||
Path: mdf.path,
|
||||
}
|
||||
if existing, ok := existingByTitle[mdf.title]; ok {
|
||||
docRoute.State = existing.State
|
||||
docRoute.Description = existing.Description
|
||||
docRoute.IconPath = existing.IconPath
|
||||
}
|
||||
children = append(children, docRoute)
|
||||
}
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -1304,7 +1304,7 @@ export interface Chat {
|
||||
/**
|
||||
* LastInjectedContext holds the most recently persisted
|
||||
* injected context parts (AGENTS.md files and skills). It
|
||||
* is updated only when context changes — first workspace
|
||||
* is updated only when context changes, on first workspace
|
||||
* attach or agent change.
|
||||
*/
|
||||
readonly last_injected_context?: readonly ChatMessagePart[];
|
||||
|
||||
Reference in New Issue
Block a user