Files
coder/docs/ai-coder/agents/tasks-to-chats-migration.md
T
Nick Vigilante c84aa564ba docs: normalize code-fence languages for Shiki compatibility (#27161)
Normalizes non-standard code-fence language tags across `docs/**` so a
strict highlighter (Shiki, used by Fumadocs) won't fail the build on an
unrecognized language, and unifies redundant synonym tags onto one
canonical form per language. The current renderer (Speed-Highlight)
detects the language from the code content, not the fence label, so this
drift wasn't visible until now.

## Changes

- `hcl` -> `tf` (199 fences, including indented ones nested in
numbered/bulleted lists). Shiki ships `hcl` and `terraform` as two
distinct grammars (not aliases); every `hcl`-tagged fence in `docs/**`
is actually Terraform resource/data/provider syntax, so the more
specific `terraform` grammar is correct for all of them. `tf` is Shiki's
own alias for that grammar, and it's also what GitHub's own markdown
renderer resolves to the same HCL/Terraform highlighting.
- `pwsh`/`powershell` -> `ps1`. Both `ps` and `ps1` are registered
PowerShell aliases in Shiki, but on GitHub's renderer only `.ps1` is a
registered file extension (`.ps` isn't), so `ps1` renders identically to
`powershell` there today while bare `ps` would silently lose
highlighting.
- `env` -> `dotenv` (a dedicated Shiki grammar for `KEY=VALUE` files)
- `text`/`output`/`none`/`url` -> `txt`. Same built-in plain-text
fallback either way, just shorter.
- `Dockerfile` -> `dockerfile` (lowercase)
- `bash`/`shell` -> `sh` (732 fences). Shiki and GitHub both alias all
three to a single shell grammar; this was already the style guide's
stated preference, just not enforced across the existing corpus until
now.
- `markdown` -> `md` (4 fences). Alias of the same grammar in both Shiki
and GitHub.
- `jsonc` -> `json` (1 fence). The block has no comments or trailing
commas, so it doesn't need the comments-capable grammar.
- `ts` -> `tsx` (2 fences, `docs/about/contributing/frontend.md`).
Verified the actual content tokenizes identically under both grammars,
and a sibling block in the same file already needs `tsx` for real JSX,
so unifying to one tag is safe for this file. Documented a caveat: `tsx`
mis-tokenizes the legacy angle-bracket type-assertion syntax
(`<Type>value`), which is invalid in real `.tsx` files anyway, so use
`value as Type` instead.
- `yml` -> `yaml` (1 fence)
- Updated `docs/.style/style-guide/formatting.md` to document all
canonical tags

`promql` (2 fences) and `caddyfile` (2 fences) are left as-is. Shiki
doesn't bundle a grammar for either, so they need a custom grammar
registration when the site adopts Shiki, rather than degrading to `txt`.
Tracked as follow-up work under DOCS-118 and
[DOCS-544](https://linear.app/codercom/issue/DOCS-544/vendor-a-local-promql-grammar-for-shiki-syntax-highlighting)
(promql).

Does not touch `offlinedocs/`.

Linear:
[DOCS-476](https://linear.app/codercom/issue/DOCS-476/normalize-docs-code-fence-languages-de-risk-shikifumadocs)

<details>
<summary>How the fence tags were verified</summary>

Each tag was tested against a real `shiki@latest` highlighter instance
(`codeToHtml`/`codeToTokens`) and cross-checked against GitHub's
`@wooorm/starry-night` grammar sources (the renderer that actually
displays these `.md` files today, in repo browsing and PR diffs), since
that's what determines whether brevity is safe before Shiki adoption:

```text
FAIL  env        -- Language `env` is not included in this bundle.
FAIL  Dockerfile -- Language `Dockerfile` is not included in this bundle.
FAIL  promql     -- Language `promql` is not included in this bundle.
FAIL  caddyfile  -- Language `caddyfile` is not included in this bundle.
FAIL  pwsh       -- Language `pwsh` is not included in this bundle.
FAIL  output     -- Language `output` is not included in this bundle.
```

`hcl` doesn't error in Shiki, since it's a real grammar, but that's
exactly the trap: it was silently rendering every fence with the generic
HCL grammar instead of the Terraform-specific one. Every `hcl`-tagged
fence in `docs/**` was manually checked against `origin/main` and is
genuinely Terraform content.

For `ts`/`tsx`, tokenizing the actual doc content confirmed identical
output under both grammars; a synthetic test with the legacy
angle-bracket cast syntax confirmed `tsx` degrades on that specific
construct, which the style guide now calls out.

The first normalization pass only matched fence tags at column 0
(`^```tag$`), missing tags indented inside numbered/bulleted lists. A
follow-up pass caught the remaining occurrences at any indentation
level.

</details>


---

*This PR description and the underlying changes were prepared with Coder
Agents assistance.*
2026-07-15 14:07:09 -04:00

31 KiB

Migrating from the Tasks API to the Chats API

The Tasks API (/api/v2/tasks) and the Chats API (/api/experimental/chats) serve similar goals (programmatic access to AI-powered coding agents) but they differ significantly in architecture, capabilities, and usage patterns.

This guide walks you through updating your integrations from the Tasks API to the Chats API.

Note

The Chats API is experimental in current Coder releases. Endpoints live under /api/experimental/chats and may change without notice until the feature graduates to GA.

When to migrate

Coder Tasks is being deprecated. Support continues on the ESR release and through Coder v2.36. See the deprecation notice on the Coder Tasks page for the full timeline.

If you currently run workflows on the Tasks API, you should plan to migrate to the Chats API and Coder Agents. Coder Agents runs the agent loop in the Coder control plane rather than inside the workspace, and is the supported path going forward.

The two systems are not interchangeable. Tasks and Chats are separate resources with separate APIs, so plan to update your integrations rather than expecting a drop-in replacement.

Key architectural differences

Before mapping individual endpoints, understand the structural changes:

Aspect Tasks API Chats API
Agent execution Agent runs inside the workspace (via AgentAPI) Agent loop runs in the control plane
LLM credentials Injected into workspace environment Stored in control plane only; never enters the workspace
Workspace provisioning You specify a template_version_id at creation The agent auto-selects a template and provisions on demand
Template requirements Requires coder_ai_task resource, coder_task data source, and an agent module Any template with a clear description works
Chat state Stored in the workspace (AgentAPI state file) Persisted in the Coder database
Conversation model Single prompt with optional follow-up input Multi-turn chat with message history, queuing, and editing
Real-time updates HTTP polling (GET .../logs) WebSocket streaming (GET .../stream)
Sub-agents Not supported Built-in sub-agent delegation

Endpoint mapping

The table below maps each Tasks API endpoint to its Chats API equivalent.

Operation Tasks API Chats API
List GET /api/v2/tasks GET /api/experimental/chats
Create POST /api/v2/tasks/{user} POST /api/experimental/chats
Get by ID GET /api/v2/tasks/{user}/{task} GET /api/experimental/chats/{chat}
Delete DELETE /api/v2/tasks/{user}/{task} PATCH /api/experimental/chats/{chat} with {"archived": true}
Send follow-up POST /api/v2/tasks/{user}/{task}/send POST /api/experimental/chats/{chat}/messages
Update input PATCH /api/v2/tasks/{user}/{task}/input PATCH /api/experimental/chats/{chat}/messages/{message}
Get logs / stream GET /api/v2/tasks/{user}/{task}/logs GET /api/experimental/chats/{chat}/stream (WebSocket)
Pause POST /api/v2/tasks/{user}/{task}/pause POST /api/experimental/chats/{chat}/interrupt
Resume POST /api/v2/tasks/{user}/{task}/resume POST /api/experimental/chats/{chat}/messages (send a new message)
Watch all n/a GET /api/experimental/chats/watch (WebSocket)
Get messages n/a GET /api/experimental/chats/{chat}/messages
List models n/a GET /api/experimental/chats/models
Upload file n/a POST /api/experimental/chats/files

Migration steps

1. Configure an LLM provider

With Tasks, LLM credentials are injected into the workspace as environment variables (e.g. ANTHROPIC_API_KEY). With Coder Agents, credentials are configured once in the control plane:

  1. Navigate to Admin settings > AI and select Providers.
  2. Add or update a provider with its credentials and upstream endpoint, then save it.
  3. Navigate to Admin settings > AI > Models, add at least one model, and set it as the default.

You no longer pass API keys in template variables or workspace environment. See https://coder.com/docs/ai-coder/agents/getting-started for more information.

2. Update task creation calls

Tasks API. You specify the user, template version, and a prompt string:

# Tasks API: create a task
curl -X POST https://coder.example.com/api/v2/tasks/me \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template_version_id": "<template-version-uuid>",
    "input": "Fix the failing tests in the auth service"
  }'

Chats API. You send structured content parts. No template or user path segment is required:

# Chats API: create a chat
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": "Fix the failing tests in the auth service"}
    ]
  }'

Key differences:

  • The {user} path parameter is removed. The authenticated user is inferred from the session token.
  • organization_id is required in the request body. The caller must be a member of that organization.
  • The prompt is now an array of ChatInputPart objects (supporting text, file, and file-reference types) instead of a plain string.
  • template_version_id and template_version_preset_id are removed. The agent selects a template automatically based on the prompt and available template descriptions. To pin to a specific workspace, pass workspace_id instead.
  • Optionally pass model_config_id to override the default model, or mcp_server_ids to attach MCP servers.

3. Update follow-up message calls

Tasks API. Follow-ups use the send endpoint with a plain string:

# Tasks API: send input
curl -X POST https://coder.example.com/api/v2/tasks/me/my-task/send \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"input": "Now also update the integration tests"}'

Chats API. Follow-ups use the messages endpoint with content parts:

# Chats API: send a message
curl -X POST \
  https://coder.example.com/api/experimental/chats/$CHAT_ID/messages \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": [
      {"type": "text", "text": "Now also update the integration tests"}
    ]
  }'

The Chats API supports message queuing. If the agent is busy, the message is queued automatically and delivered when the agent finishes its current step. The response includes a queued field indicating whether the message was delivered immediately or queued.

4. Switch from log polling to WebSocket streaming

Tasks API. You poll for logs:

# Tasks API: get logs
curl https://coder.example.com/api/v2/tasks/me/my-task/logs \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

Chats API. You open a one-way WebSocket connection:

GET wss://coder.example.com/api/experimental/chats/{chat}/stream

The WebSocket sends JSON envelopes with a type field ("ping", "data", or "error"). Data envelopes contain batches of events:

Event 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
queue_update The queued message list changed

Use after_id as a query parameter when reconnecting to skip messages the client already has.

5. Update status handling

Task and chat statuses use different values. The Chats API status set is defined in codersdk.ChatStatus:

Tasks API status Chats API status Notes
pending running Chats have no separate queued state; a chat that hasn't been picked up yet reports running.
running running Agent is actively working.
complete waiting Idle. Newly created, finished successfully, or interrupted. This is the default idle state.
paused n/a The Tasks API pause stops the workspace; the Chats API equivalent is interrupt plus separate workspace lifecycle.
failed error Agent encountered an error.
n/a requires_action Agent invoked a client-provided tool and is waiting for the result before continuing.
n/a interrupting An interrupt was requested and the agent is winding down the current run.

The Chats API uses waiting as the default idle state (not complete). A chat enters waiting when it is first created (before any message is queued) and again whenever a run finishes or is interrupted, so treat waiting as "the agent is not currently working" rather than only "the agent just finished."

6. Replace delete with archive

The Tasks API uses DELETE to remove a task. The Chats API uses archiving:

- curl -X DELETE https://coder.example.com/api/v2/tasks/me/my-task \
-   -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

+ curl -X PATCH https://coder.example.com/api/experimental/chats/$CHAT_ID \
+   -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
+   -H "Content-Type: application/json" \
+   -d '{"archived": true}'

Archived chats can be restored by setting archived to false.

7. Replace pause/resume with interrupt and messaging

Tasks API. Pause and resume stop and start the workspace:

# Tasks API
curl -X POST \
  https://coder.example.com/api/v2/tasks/me/my-task/pause \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

curl -X POST \
  https://coder.example.com/api/v2/tasks/me/my-task/resume \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

Chats API. Interrupt stops the current agent loop. Sending a new message resumes processing:

# Chats API: interrupt
curl -X POST \
  https://coder.example.com/api/experimental/chats/$CHAT_ID/interrupt \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

# Chats API: resume by sending a new message
curl -X POST \
  https://coder.example.com/api/experimental/chats/$CHAT_ID/messages \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": [
      {"type": "text", "text": "Continue where you left off"}
    ]
  }'

In the Tasks API, pausing stops the workspace and frees compute. In the Chats API, interrupt stops the agent loop in the control plane; the workspace may remain running. The workspace lifecycle is managed independently.

8. Update GitHub Actions integrations

If you use the Create Task Action GitHub Action, replace it with the dedicated coder/create-agent-chat-action. It handles the API call, the GitHub user lookup, and the optional issue comment, so most existing workflows can swap one uses: line and rename a few inputs.

We are actively shipping new features for create-agent-chat-action, so pin to a major version (for example @v0) and watch the releases for updates.

# .github/workflows/triage-bug.yaml
jobs:
  coder-create-task:
    runs-on: ubuntu-latest
    if: github.event.label.name == 'coder'
    steps:
-     - name: Coder Create Task
-       uses: coder/create-task-action@v0
-       with:
-         coder-url: ${{ secrets.CODER_URL }}
-         coder-token: ${{ secrets.CODER_TOKEN }}
-         coder-organization: "default"
-         coder-template-name: "my-template"
-         coder-task-name-prefix: "gh-task"
-         coder-task-prompt: >-
-           Use the gh CLI to read
-           ${{ github.event.issue.html_url }},
-           fix the issue, and create a PR.
-         github-user-id: ${{ github.event.sender.id }}
-         github-issue-url: ${{ github.event.issue.html_url }}
-         github-token: ${{ github.token }}
-         comment-on-issue: true
+     - name: Coder Create Agent Chat
+       uses: coder/create-agent-chat-action@v0
+       with:
+         coder-url: ${{ secrets.CODER_URL }}
+         coder-token: ${{ secrets.CODER_TOKEN }}
+         chat-prompt: >-
+           Use the gh CLI to read
+           ${{ github.event.issue.html_url }},
+           fix the issue, and create a PR.
+         github-user-id: ${{ github.event.sender.id }}
+         github-issue-url: ${{ github.event.issue.html_url }}
+         github-token: ${{ github.token }}
+         comment-on-issue: true

Key differences from the Tasks GHA:

  • No coder-template-name or coder-task-name-prefix. The agent auto-provisions a workspace; pass workspace-id if you want to pin to an existing workspace instead.
  • The prompt input is renamed from coder-task-prompt to chat-prompt.
  • LLM credentials are no longer passed through the template. They are configured in the Coder control plane.
  • Identify the user with github-user-id (the action resolves it to a Coder user via the GitHub OAuth link) or with coder-username directly.

See the action README for the full input and output reference, including the existing-chat-id input for sending follow-up messages on a previous chat.

Template recommendations

Note

This section contains recommendations that may evolve as Coder Agents matures. Review these against your deployment requirements.

With Coder Tasks, every task-capable template requires specific Terraform resources (coder_ai_task, coder_task, agent modules, and LLM API keys). With Coder Agents, templates no longer need any of these. The agent runs in the control plane and treats the workspace as plain compute.

However, we still recommend creating dedicated templates for agent workloads rather than reusing your standard developer templates unchanged. The reasons are different from Tasks, but the principle holds:

Why dedicated agent templates still matter

  • Network boundaries. Agent workspaces inherit whatever network access the template allows. Because the agent does not need outbound access to LLM providers (that happens in the control plane), you can lock down agent templates to only reach the Coder control plane and your git provider. Standard developer templates typically allow broader access.
  • No IDE tooling overhead. The agent connects via the workspace daemon's HTTP API, not through VS Code or JetBrains. Removing IDE extensions, desktop environments, and similar tooling from agent templates reduces image size and startup time.
  • Scoped credentials. Agent workloads may warrant more restrictive credentials than interactive developer sessions. A dedicated template lets you provide a separate, narrower-scoped git token or service account without affecting your developers' workflow.
  • Cost control. Agent workspaces can often use smaller compute resources than developer workspaces since they don't need to run IDEs, language servers, or other interactive tooling. A dedicated template lets you right-size the infrastructure.

What to include in agent templates

  • Clear descriptions. The agent selects templates by reading names and descriptions. Include the target language, framework, repository, and type of work. For example: "Python backend services for the payments repo. Includes Poetry, Python 3.12, and PostgreSQL."
  • Pre-installed dependencies. Language runtimes, build tools, git, and project-specific dependencies should be baked into the image. Time the agent spends installing tools is time not spent on the task.
  • Git configuration. Ensure git is configured with credentials and author information so the agent can commit and push without additional setup.
  • Minimal parameters. Use sensible defaults so the agent can provision workspaces without guessing. Avoid required parameters with opaque identifiers.

What to remove from migrated task templates

If you are converting an existing task template for use with Coder Agents, you can safely remove the Tasks-specific Terraform resources. They are unused when the chat is driven by the Chats API:

  terraform {
    required_providers {
      coder = {
        source  = "coder/coder"
-       version = ">= 2.13"
+       version = ">= 2.13"
      }
    }
  }

- data "coder_task" "me" {}
-
- resource "coder_ai_task" "task" {
-   app_id = module.claude-code.task_app_id
- }
-
- module "claude-code" {
-   source         = "registry.coder.com/coder/claude-code/coder"
-   version        = "4.0.0"
-   agent_id       = coder_agent.main.id
-   ai_prompt      = data.coder_task.me.prompt
-   claude_api_key = var.anthropic_api_key
- }
-
- variable "anthropic_api_key" {
-   type        = string
-   description = "Anthropic API key"
-   sensitive   = true
- }

  resource "coder_agent" "main" {
    os   = "linux"
    arch = "amd64"
+   # No agent modules, no AgentAPI, no LLM keys needed.
+   # The Coder Agents control plane handles the agent loop.
  }

Tip

You do not have to remove these resources immediately. Templates can serve both Tasks and Chats simultaneously during a transition period. The Tasks-specific resources are simply unused when work comes through the Chats API.

See Template Optimization for the full guide on writing discoverable descriptions, configuring network boundaries, scoping credentials, and pre-installing dependencies.

Pre-creating a workspace for deterministic results

Letting the agent pick a template and provision a workspace works well for exploratory chats. If your workflow requires deterministic results like:

  • Automations
  • Recurring processes
  • Generally any case that needs a known reproducible environment

pre-create the workspace yourself and attach it when you create the chat.

The pattern is two API calls:

  1. Create a workspace from a specific template via POST /api/v2/users/{user}/workspaces. You control the template, the version, and any rich parameters.
  2. Create the chat with workspace_id set to the workspace you just created. The agent runs against that workspace instead of selecting one heuristically.
# 1. Provision the workspace from the exact template you want.
WORKSPACE_ID=$(curl -s -X POST \
  https://coder.example.com/api/v2/users/me/workspaces \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "<your-agent-template-uuid>",
    "name": "agent-run-${GITHUB_RUN_ID}"
  }' | jq -r '.id')

# 2. Create the chat bound to that workspace.
curl -s -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>\",
    \"workspace_id\": \"$WORKSPACE_ID\",
    \"content\": [
      {\"type\": \"text\", \"text\": \"Fix the failing tests in the auth service\"}
    ]
  }"

This pattern is the closest analogue to the Tasks API behavior of template_version_id plus coder-template-name: you decide which template runs, the agent decides what to do inside it. The same approach works from the coder/create-agent-chat-action GHA, which exposes the same pin via its workspace-id input.

How to test your migration

After completing the migration steps above, walk through these checks to confirm the Chats API integration is working end-to-end.

1. Confirm LLM provider connectivity

List available models to verify at least one provider is configured and reachable:

curl -s https://coder.example.com/api/experimental/chats/models \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" | jq '.[].display_name'

If this returns an empty list or an error, revisit Step 1: Configure an LLM provider.

2. Create a chat and confirm the response

Create a simple chat that does not require a workspace:

curl -s -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": "What is 2 + 2?"}]
  }' | jq '{id, status, title}'

You should receive a Chat object with status set to "running", since new chats begin processing immediately. Save the id for subsequent steps.

3. Stream the response

Open a WebSocket connection to verify the agent processes the prompt and returns a response. Using websocat:

websocat -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  "wss://coder.example.com/api/experimental/chats/$CHAT_ID/stream"

You should see JSON envelopes with "type": "data" containing message_part and status events. The chat should eventually reach "waiting" status, indicating the agent completed its response.

4. Send a follow-up message

Verify multi-turn conversation works:

curl -s -X POST \
  "https://coder.example.com/api/experimental/chats/$CHAT_ID/messages" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "content": [{"type": "text", "text": "Now multiply that by 10"}]
  }' | jq '{queued}'

The response should include "queued": false (delivered immediately) or "queued": true (agent was busy. The message is queued and will be processed next).

5. Test workspace provisioning

Create a workspace from your converted agent template through the standard Coder UI, then attach it to a new chat from the chat composer:

  1. In the Coder dashboard, create a workspace from the agent template you migrated.
  2. Open Agents and start a new chat.
  3. In the composer, use the workspace picker to attach the workspace you just created.
  4. Send a prompt that exercises the workspace, for example: "List the files in the root directory of this workspace."

The response stream should show the agent invoking workspace tools (such as execute) against the attached workspace. After the chat finishes, verify the chat is bound to the workspace via the API:

curl -s "https://coder.example.com/api/experimental/chats/$CHAT_ID" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" | jq '{workspace_id, status}'

A workspace_id matching the workspace you attached confirms the chat is driving that workspace end-to-end. Auto-provisioning from the chat flow is also supported but is easier to verify once the manual-attach path is working.

6. Verify interrupt works

Start a long-running chat and interrupt it:

curl -s -X POST \
  "https://coder.example.com/api/experimental/chats/$CHAT_ID/interrupt" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN"

Then confirm the chat status returns to "waiting":

curl -s "https://coder.example.com/api/experimental/chats/$CHAT_ID" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" | jq '.status'

7. Validate archive and restore

# Archive
curl -s -X PATCH \
  "https://coder.example.com/api/experimental/chats/$CHAT_ID" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"archived": true}'

# Confirm it no longer appears in the default list
curl -s "https://coder.example.com/api/experimental/chats" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  | jq --arg id "$CHAT_ID" '[.[] | select(.id == $id)] | length'
# Should return 0

# Restore
curl -s -X PATCH \
  "https://coder.example.com/api/experimental/chats/$CHAT_ID" \
  -H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"archived": false}'

Quick checklist

Use this checklist to confirm each part of your integration:

  • At least one LLM model is configured and returned by /chats/models
  • POST /chats creates a chat and returns a valid Chat object
  • WebSocket stream at /chats/{chat}/stream delivers events
  • Follow-up messages via /chats/{chat}/messages are accepted
  • Chat attached to a workspace from the converted template runs tools against that workspace
  • POST /chats/{chat}/interrupt stops the agent and returns to waiting
  • Archive and restore via PATCH /chats/{chat} works
  • (If applicable) GitHub Actions workflow creates chats successfully

Features available only in the Chats API

The Chats API includes capabilities that have no equivalent in the Tasks API:

Feature Description
WebSocket streaming Real-time event stream via GET /chats/{chat}/stream instead of HTTP polling
Watch all chats GET /chats/watch pushes events for all chats owned by the user
Message editing PATCH /chats/{chat}/messages/{message} to edit a sent message and re-process
Message queuing Follow-up messages are automatically queued when the agent is busy
File uploads Attach images via POST /chats/files and reference them in messages
Model selection GET /chats/models to discover models; override per-chat or per-message
MCP server attachment Attach MCP servers to a chat for tool augmentation
Labels Key-value metadata on chats for filtering (label query parameter)
Sub-agents Agent can spawn child agents for parallel work
Diff/PR tracking GET /chats/{chat}/diff returns change tracking and PR metadata
Title regeneration POST /chats/{chat}/title/regenerate
Pinning Pin and reorder chats via the pin_order field
Automatic workspace provisioning No workspace needed for Q&A. Provisioned only when the agent needs to act

Response schema changes

The Tasks API returns a Task object with workspace-centric fields. The Chats API returns a Chat object with conversation-centric fields:

Tasks API field Chats API equivalent Notes
id id Both are UUIDs
initial_prompt First message in GET /chats/{chat}/messages Prompt is a message, not a top-level field
display_name title Auto-generated or set via PATCH
status status Different enum values (see status table above)
current_state Latest status event from the stream No equivalent top-level field
workspace_id workspace_id Nullable in Chats. May be null if no workspace was provisioned
workspace_status n/a Manage workspace lifecycle separately
template_id n/a Not exposed; the agent selects templates internally
owner_id owner_id Same concept
name n/a Chats use id for identification, not human-readable names

CLI changes

The Tasks CLI (coder task) remains separate from the Coder Agents Chats API. Coder no longer ships an interactive Coder Agents TUI. Use the web UI for interactive chat and direct API calls for automation.

Tasks CLI Chats equivalent
coder task create Web UI or POST /chats
coder task list Web UI or GET /chats
coder task logs GET /chats/{chat}/stream (WebSocket)
coder task pause POST /chats/{chat}/interrupt
coder task resume Send a follow-up message to the chat