docs: generate Chats API docs from swagger annotations (#24830)

This commit is contained in:
david-fraley
2026-05-05 14:52:54 -04:00
committed by GitHub
parent cfce751b8a
commit e7360da974
13 changed files with 8597 additions and 422 deletions
-3
View File
@@ -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
+1805
View File
File diff suppressed because it is too large Load Diff
+1698
View File
File diff suppressed because it is too large Load Diff
+183
View File
@@ -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
View File
@@ -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"`
-406
View File
@@ -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).
+3 -3
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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",
+2732
View File
File diff suppressed because it is too large Load Diff
+2152
View File
File diff suppressed because it is too large Load Diff
+13
View File
@@ -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)
}
+1 -1
View File
@@ -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[];