# Chat sessions

A chat session is one piece of work on a project. Once started, it owns a branch, a sandbox, a conversation and, once the agent opens one, a pull request. Create one with a first message to start work now, or with `status: "backlog"` to save it for later. Change its title, status, model, priority or Assignee with `PUT`, and its labels with `PUT .../chat-sessions/{sessionId}/labels`, or add and remove some with `PATCH` on the same path.

| Method | Path                                                                                                                                                                                                                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| GET    | `/api/chat-sessions?kind=&includePrompt=&includeMergeAgentRuns=&completedSince=&workspace=&project=&pullRequest=&projects=&labels=&ids=&status=&owners=&priorities=&q=&createdBy=&includeArchived=&limit=&cursor=`        | List chat sessions across every workspace you belong to, each with its workspace, project and URL. q narrows them to titles containing it, ids to the sessions it names. Merge agent run sessions are left out unless includeMergeAgentRuns=true or ids names them. An agent token reads its own workspace only. Each session carries waitingForDeploy: true while it waits for the Deploy to finish, false once the Deploy ends, before its sandbox starts; goAheadSha, the head its open pull request has the go-ahead on; and mergeRule, its Assignee's Merge rule.                                                                                                                                                                                                    |
| GET    | `/api/chat-sessions/search?q=`                                                                                                                                                                                            | Search chat sessions by title across every workspace you belong to. An agent token searches its own workspace.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| GET    | `/api/find/{query}`                                                                                                                                                                                                       | Find sessions and tasks by pull request number, session, task or Sensei Thread id, or title words (every word, any case), across what `kosuke sessions list` reaches, archived and ended ones included. One row per match, at most 20, exact matches first, then live before ended: match, workspaceId, workspaceName, projectId, projectName, sessionId, sessionTitle, status, threadId (your own Sensei Thread that follows it), taskId, pullRequestNumber, headSha and url. A task with no session has a null sessionId. An agent token finds within its own workspace.                                                                                                                                                                                                |
| POST   | `/api/projects/{projectId}/attachments`                                                                                                                                                                                   | Stage one private message attachment. Returns data.reference for an attachment slot in a subsequent message to this project, using the same credential, within one hour.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| POST   | `/api/projects/{projectId}/attachments/uploads`                                                                                                                                                                           | Sign a direct upload of one video (MP4, MOV or WebM, up to 10 MB and 3 minutes). Returns data.uploadUrl, which takes one PUT of the raw file with data.headers within 30 minutes, and data.reference for an attachment slot in a subsequent message to this project, using the same credential, within one hour.                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| GET    | `/api/projects/{projectId}/chat-sessions?status=&labels=&priorities=&search=&limit=&cursor=&sortBy=&sortOrder=&sortByPriority=&excludeArchived=&excludeBacklog=&owners=&createdBy=&includePrompt=&includeMergeAgentRuns=` | List chat sessions. Without limit the whole list comes back as data; with it, one page plus meta.pagination. status, priorities and owners are comma-separated strings. Merge agent run sessions are left out unless includeMergeAgentRuns=true. Each session carries waitingForDeploy: true while it waits for the Deploy to finish, false once the Deploy ends, before its sandbox starts.                                                                                                                                                                                                                                                                                                                                                                              |
| POST   | `/api/projects/{projectId}/chat-sessions`                                                                                                                                                                                 | Start a chat session, or file a backlog one with status=backlog. Title or content is required; a backlog session needs content. Attachments ride along as multipart. From a sandbox or a Cron Run, only status=backlog, and reason is required: one line on why the ticket exists. From a Cron Run, the system user files it and is its Assignee, and a model the workspace cannot run falls back to the Cron's: model says which one it will run on. A backlog session answers shareUrl, the link to give for it.                                                                                                                                                                                                                                                        |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}`                                                                                                                                                                     | Read one chat session with its URL, latest job, pull request and taskId (the linked task in the project workspace, or null). A Backlog session also carries its promptRevision, which `kosuke sessions prompt update --if-revision` and `kosuke sensei tickets merge` name for each ticket an edit was written from. Each session carries waitingForDeploy: true while it waits for the Deploy to finish, false once the Deploy ends, before its sandbox starts. senseiThread is your Sensei thread that follows the session (id, title, url, and for a Sensei Turn thisThread: whether it is that Turn's own), or null: its events and cards go there.                                                                                                                   |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}`                                                                                                                                                                     | Change title, status, model, Assignee, priority, Sandbox size (sandboxSize) or App stack (appStack). Which changes are legal depends on the current status; the handler decides. sandboxSize and appStack change only while the session is in the Backlog: once it started they are fixed, and 409 SANDBOX_SETTINGS_LOCKED refuses them.                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| DELETE | `/api/projects/{projectId}/chat-sessions/{sessionId}`                                                                                                                                                                     | Delete a chat session. Sensei deletes only a Backlog ticket, refused with 409 NOT_IN_BACKLOG once it started, whatever started the Turn.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/activate`                                                                                                                                                            | Start a backlog session: queue its saved prompt as the first turn, which runs once the sandbox is ready. Answers before the sandbox exists; a sandbox that fails to start later leaves the session Active with a failed first turn. With --skill ship the first turn is /kosuke:ship on that prompt. From an agent (a sandbox, a Cron Run or Sensei) it is also refused with 403 ASSIGNED_TO_OTHER when another member is the Assignee; a ticket that is unassigned or assigned to the system user passes that check and meets the same project access and status checks as when a member starts it. From Sensei, the session moves to the thread of the Turn that started it.                                                                                            |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/desktop`                                                                                                                                                             | Read a chat session's Desktop: its state (off, starting, ready or unavailable), the page url that streams it while ready, who controls it (agent or member), and the machine, the sandbox's Linux desktop.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/desktop`                                                                                                                                                             | Start a chat session's Linux desktop if it is not up, and read its Desktop. A no-op once it is up. The session's sandbox must be running.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}/desktop/control`                                                                                                                                                     | Hand a chat session's desktop to a member, or back to the agent. While a member controls it, the agent's desktop tools refuse. Answers the Desktop as it is after the change. Refused with 409 DESKTOP_NOT_RUNNING while the session's sandbox is not up.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/env-sync`                                                                                                                                                            | Deliver the project's saved environment variables, with its services and setup commands, to a chat session's running sandbox and restart its setup and services, so a variable added after the sandbox booted reaches them. Answers status synced with the variable names, never their values, once the services answer again, or status running when setup outlasts ten minutes and the sync goes on. Refused, never queued: 409 TURN_RUNNING while a turn runs, SANDBOX_NOT_RUNNING when the sandbox is not up (it is not woken), SANDBOX_PROVISIONING or SANDBOX_NOT_READY while it is created or boots, APP_STACK_STARTING during an App stack start and SANDBOX_BUSY while another update changes it; 422 ENV_SYNC_FAILED when setup or a service did not come back. |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}/go-ahead`                                                                                                                                                            | Give the session's pull request the go-ahead to merge, as the session's member, on one head: goAheadSha, goAheadBy and goAheadAt. A draft pull request is first marked ready for review on GitHub as the member; when GitHub refuses, nothing is saved (503). The project's Merge agent (the merge train, when it is on) is woken and merges it only at that head. Members and their Sensei only.                                                                                                                                                                                                                                                                                                                                                                         |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/go-ahead/clear`                                                                                                                                                      | Take the session's go-ahead away, so the Merge agent does not merge its pull request until it is given again. The callers of `sessions go-ahead set`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}/labels`                                                                                                                                                              | Replace the session's Session Labels. New names join the project's labels. Session Labels stay in Sensei: none is written to the pull request.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| PATCH  | `/api/projects/{projectId}/chat-sessions/{sessionId}/labels`                                                                                                                                                              | Add and remove Session Labels, leaving the session's other labels as they are. color sets the initial project color only for newly created labels in add; existing label colors stay unchanged. Session Labels stay in Sensei: none is written to the pull request.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/messages?limit=&before=&from=&live=`                                                                                                                                 | Read a chat session conversation oldest first: the whole of it, its last limit messages, the page before an olderCursor, or everything from one; newestCursor is what a poll passes as from to read only what changed since. messageCount counts the whole conversation. A running Turn's assistant message has streaming: true and the blocks the Turn has produced so far, as the web app shows them; every other message has streaming: false. Blocks come as stored: a tool call's input keeps its short fields (inputCompacted when cut), a result over 500 characters its first 500 (resultCompacted), and a subagent its last output entry with outputCount (outputOmitted when cut). `kosuke sessions messages get` reads one message whole.                      |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/messages/{messageId}?agentOutput=`                                                                                                                                   | Read one message of a chat session with every block as recorded: whole tool inputs, results and subagent output, unless agentOutput is compact (subagent output as stored) or summary (blocks as stored, as `kosuke sessions messages list` returns them). The assistant message of a running Turn has streaming: true and the blocks the Turn has produced so far, as the web app shows them; any other message has streaming: false.                                                                                                                                                                                                                                                                                                                                    |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/preview/app-stack`                                                                                                                                                   | Start a chat session's App stack, the project's services, in its running sandbox and wait until they answer; restarts them when an earlier start left them down, a no-op while they run. A session's own agent may start its own; 409 SANDBOX_NOT_RUNNING when the sandbox is not up, 409 APP_STACK_NOT_READY while its setup still runs and 409 APP_STACK_STARTING while another start runs (retry both in a minute), 422 APP_STACK_START_FAILED with the reason.                                                                                                                                                                                                                                                                                                        |
| PATCH  | `/api/projects/{projectId}/chat-sessions/{sessionId}/prompt`                                                                                                                                                              | Rewrite the saved prompt of a backlog session, keeping or replacing its attachments. Multipart only. With ifRevision, the promptRevision `sessions get` gave, it is refused with 409 TICKET_EDITED when the prompt changed since; Sensei and a Cron Run must pass it. Send the whole new description, and only for a new goal; an agent rewrites only a ticket it filed unless the member asked.                                                                                                                                                                                                                                                                                                                                                                          |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-passed`                                                                                                                                                           | Record the commit QA passed on as the session's qaPassedSha: a member's override. `sessions qa runs finish` records it when the whole checklist passed. A pull request is done when qaPassedSha equals its head.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs`                                                                                                                                                             | Start a QA Run of the session's Testing Checklist, or continue the newest one when HEAD is its start or only its fixes moved it. Only the session's own sandbox. Answers the checklist to write back, the counts and the budget.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs/{run}`                                                                                                                                                       | Read a QA Run, or `latest`, as data: each section and item of the session's published Testing Checklist with its status and the outcome that explains it, the counts, the assessment, the history and the budget.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs/{run}/block`                                                                                                                                                 | Block what is left of a QA Run's selection with the reason, and finish it. Records no pass. Only the session's own sandbox.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs/{run}/finish`                                                                                                                                                | Finish a QA Run and record QA Passed when the whole checklist passed at the sandbox's clean HEAD; the answer's qaPassed says why not. A run that ends failed, blocked or incomplete clears a QA Passed the session's runs recorded at its head. Finishing again decides again. Only the session's own sandbox.                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs/{run}/fixes`                                                                                                                                                 | Record a fix commit of a QA Run, a commit on HEAD after the run's start: unticks the FAILED items it targets for their retest and adds them to the selection. Only the session's own sandbox.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/qa-runs/{run}/results`                                                                                                                                               | Apply one section attempt a tester or prober wrote to a QA Run: stores it, ticks or tags its items and republishes the QA Report. The same result again changes nothing. Only the session's own sandbox.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| GET    | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue`                                                                                                                                                               | List the messages waiting Up next in the session, in the order they will be sent, and whether Up next is paused.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| PATCH  | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}`                                                                                                                                             | Save an edit to your own queued message, keeping its place in Up next. New files ride along as multipart.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| DELETE | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}`                                                                                                                                             | Delete a queued message: your own, or any as a workspace owner or admin.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}/move`                                                                                                                                        | Move your own queued message to another place in Up next.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}/send`                                                                                                                                        | Send your own queued message now as a turn of its own, ahead of the rest of Up next. Only while no turn is running; it resumes a paused Up next.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}/steer`                                                                                                                                       | Send your own queued message into your running turn as a steer. The message stays Up next when the turn cannot take it.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/queue/resume`                                                                                                                                                        | Resume a session's paused Up next. Its top message is sent as soon as nothing is running.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/read`                                                                                                                                                                | Mark a chat session read, clearing its unread marker, as opening it in the app does. The assignee clears their marker; any member clears the shared marker of a session booked to the system user.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| PUT    | `/api/projects/{projectId}/chat-sessions/{sessionId}/review`                                                                                                                                                              | Record a review's verdict for the sandbox's HEAD, with the head and tree the platform reads from the checkout. Refused (409) while HEAD is not the given head, while tracked files have uncommitted changes, or once the Turn stopped. Only the session's own sandbox.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/runtime-commands`                                                                                                                                                    | Run a runtime command on the agent session: clear starts a fresh conversation, compact summarizes it, with optional focus instructions. Queued Up next while a turn or another command is running. Compact waits for the sandbox and can take a minute.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| POST   | `/api/projects/{projectId}/chat-sessions/{sessionId}/steer`                                                                                                                                                               | Send a message and steer it into your running turn. Answers delivery steer, queued (the turn could not take it), job (no turn was running) or refused (the turn was stopped). Use it for plain text up to 10,000 characters; on queued or refused, say where the message is. From Sensei, /kosuke:sync-branch is refused with 409 SYNC_OWNED_BY_MERGE_AGENT when the project is on the merge train and the pull request has the go-ahead, unless a member started the Turn: the merge agent syncs those.                                                                                                                                                                                                                                                                  |

## Create a chat session

Create a session and, when you send `content`, start its first turn in the same call.

| Field | Type | Required | What it does |
| --- | --- | --- | --- |
| `content` | string | With `status: "backlog"` | The first message. Without `status`, it starts the first turn. |
| `title` | string, 1 to 255 characters | When `content` is absent | The session's title. Left out, Sensei writes one from `content`. |
| `status` | `"backlog"` | No | Saves the session to the Backlog instead of starting it. No other value is accepted. |
| `model` | string | No | A value from [`GET /api/models`](https://kosuke.ai/docs/endpoint-reference/models.md). Left out, the session uses its Assignee's Default Model. |
| `ownerId` | string | No | Backlog only. The member id of the Assignee. Left out, a Backlog session is unassigned. A session that starts now is assigned to you. |
| `priority` | `urgent`, `high`, `medium`, `low` or `none` | No | Triage priority. Default `none`. |
| `startBranch` | string | No | The branch the session starts from, instead of the project's base branch. It must exist on GitHub. |
| `sandboxSize` | `small`, `medium`, `large` or `xlarge` | No | The Sandbox size: 2 CPU and 4 GB, 4 and 8, 8 and 16, or 16 and 24. Left out, the session boots on its project's size. |
| `appStack` | `start` or `skip` | No | `start`, the default, runs the project's services when the sandbox boots. `skip` boots setup and the agent only; see [App stack](https://kosuke.ai/docs/endpoint-reference/chat-sessions.md#app-stack). |

What you get depends on what you send:

| You send | Result |
| --- | --- |
| `content` | An Active session. Its sandbox starts and `content` runs as the first turn. The response carries `chatJobId`. |
| `content` and `status: "backlog"` | A Backlog session holding `content` as its prompt, with no branch and no sandbox. The response carries `shareUrl`, the session's link in the app. |
| `title` alone | An Active session with no turn. Wake its sandbox with [`GET /api/projects/{projectId}/preview?sessionId={sessionId}`](https://kosuke.ai/docs/endpoint-reference/previews.md) before you send the first message. |

```bash
curl -s -X POST "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions" \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"content": "Add a dark mode toggle to the settings page"}'
```

The answer is `201`:

```json
{
  "data": {
    "session": {
      "id": "3f0c8a52-6d1e-4b7a-9c2f-1e8d4a6b7c90",
      "title": "Add a dark mode toggle to the settings page",
      "status": "active",
      "priority": "none",
      "sandboxStatus": "starting",
      "latestChatJobStatus": "pending"
    },
    "chatJobId": "b7e41d09-2c5a-4f3e-8a61-0d9c7b2e5f14",
    "jobNumber": 1
  }
}
```

Follow the turn with `chatJobId`, as [Messages and turns](https://kosuke.ai/docs/endpoint-reference/messages-and-turns.md) describes.

To attach files, send `multipart/form-data` with the same fields. Add `attachmentCount` and one field per file, `attachment_0` to `attachment_19`. A message takes up to 20 files of 10 MiB (10,485,760 bytes) each; one byte more is refused.

```bash
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions" \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  -F content="The header overlaps the menu on mobile, see the screenshot" \
  -F attachmentCount=1 \
  -F attachment_0=@header.png
```

When the total file bytes exceed 20 MiB, stage each file with an authenticated `POST /api/projects/{projectId}/attachments`. Each request takes `multipart/form-data` with `attachmentCount=1` and `attachment_0=<File>` and returns `{ "data": { "reference": "..." } }`. Then send the ordinary message form with the same `attachmentCount` and each opaque reference string in its original `attachment_<i>` slot. Use at most three concurrent uploads. Do not send the message if any upload fails.

The CLI discovers the staging path from the operation's `x-kosuke-attachment-upload-path` OpenAPI extension and does this automatically for session creation, sends, prompt edits, queued-message edits and tasks filed with a hint project. Small batches remain inline. Operations without the extension, and older servers returning 404 or 405 on the first staging request, use inline uploads and remain subject to that server's request-body limit.

References are temporary and scoped to the authenticated caller, project and attachment purpose. They are not storage keys or URLs. The server rechecks access and file policy when consuming them; staging does not grant permission to send. Unconsumed uploads from failures, cancellation or abandoned clients require the server's bounded expiry cleanup. Clients must not delete committed message files after a lost response. Queued messages retain their committed attachments independently of staging expiry.

The CLI takes the fields as flags and files with `--attach`:

```bash
kosuke sessions create $PROJECT_ID --content "Fix the header on mobile" --attach header.png
kosuke sessions create $PROJECT_ID --content "Rename the billing page" --status backlog
```

## Start a Backlog session

`POST .../activate` starts a Backlog session. It takes an optional JSON body. `skill: "ship"` runs the saved prompt as `/kosuke:ship`. Sensei gives the session a branch and a sandbox and runs the saved prompt as the first turn. Starting an unassigned session makes you its Assignee. The session boots with the Sandbox size and App stack it holds. The request answers once the first turn is queued, before the sandbox is ready, so `sandboxStatus` reads `starting`.

```bash
curl -s -X POST "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID/activate" \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

The answer is `201` with `session`, `chatJobId` and `jobNumber`, as soon as the first turn is queued. The sandbox is still starting. If it then fails to start, the session stays Active with `sandboxStatus: "error"`, and its first turn fails with `errorCode: "sandbox_failed"` and the reason. Read the turn as [Follow a turn](https://kosuke.ai/docs/api/turns.md) shows, and send the turn again once the cause is fixed. A session that is already Active or Under Review answers `200` with the same shape, so a retry is safe. A Completed or Archived session answers `400`. The CLI command is `kosuke sessions start <projectId> <sessionId>`. For the whole flow, see [Add work to the Backlog and start it later](https://kosuke.ai/docs/recipes/backlog-now-start-later.md).

## List chat sessions

There are two lists. `GET /api/projects/{projectId}/chat-sessions` reads one project. `GET /api/chat-sessions` reads every workspace you belong to.

Parameters for one project (`kosuke sessions list`):

| Parameter | What it does |
| --- | --- |
| `status` | Comma-separated status values, such as `active,under-review`. |
| `priorities` | Comma-separated priorities. |
| `owners` | Comma-separated member ids of Assignees, plus `me`, `none` (unassigned) and `any`. |
| `labels` | A JSON array of Session Labels. A session matches when it carries any of them. |
| `search` | Keeps sessions whose title contains the text, ignoring case. |
| `createdBy` | `agent` keeps the sessions Sensei filed. `member` keeps the rest. |
| `excludeArchived` | `true` drops Archived sessions. Ignored when `status` is set. |
| `excludeBacklog` | `true` drops Backlog sessions. |
| `sortBy` | `activity` orders by latest activity, the default. `created` orders by creation time. `priority` orders by priority, then creation time. |
| `sortOrder` | `asc` or `desc`. Without it, `activity` and `created` list the newest first, and `priority` lists urgent first, then the oldest created. `desc` on `priority` reverses that: no priority first, then the newest created. |
| `sortByPriority` | `true` is the same as `sortBy=priority`. Kept for older clients; a `400` beside any other `sortBy`. |
| `limit` | Page size, up to 100. Without it, the whole list comes back in one response. |
| `cursor` | `meta.pagination.nextCursor` from the previous page. Needs `limit` and the same `sortBy` and `sortOrder`. |

Parameters across workspaces (`kosuke sessions list`):

| Parameter | What it does |
| --- | --- |
| `workspace` | One workspace id. You get `403` if you are not a member of it. |
| `project` | One project id. |
| `ids` | Up to 100 comma-separated session ids. |
| `status`, `owners`, `priorities`, `createdBy` | As in the project list. |
| `labels` | JSON array of up to 20 label names, such as `["bug","ui"]`. Matches any selected label; names are trimmed, normalized to lowercase and deduplicated. `[]` means no filter. Invalid JSON or names return 400 `Invalid labels filter`. Counts and every page follow the filter. |
| `q` | Keeps sessions whose title contains the text, ignoring case. |
| `includeArchived` | `true` keeps Archived sessions, which are left out by default. Ignored when `status` is set. |
| `limit` | Page size. Default 50, up to 100. |
| `cursor` | `meta.pagination.nextCursor` from the previous page. |

Each row carries its `workspaceName`, `projectName` and `url`. What waits on you is a card in a Sensei thread, which `kosuke sensei cards list` lists.

```bash
curl -s "https://app.kosuke.ai/api/chat-sessions?owners=me&limit=1" \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": [
    {
      "id": "3f0c8a52-6d1e-4b7a-9c2f-1e8d4a6b7c90",
      "title": "Add a dark mode toggle to the settings page",
      "status": "active",
      "projectName": "storefront",
      "workspaceName": "Acme",
      "url": "https://app.kosuke.ai/projects/…"
    }
  ],
  "meta": { "pagination": { "pageSize": 1, "total": 1, "hasMore": false, "nextCursor": null } }
}
```

## Status values

| Value | In the app | What it means |
| --- | --- | --- |
| `backlog` | Backlog | Saved and not started. It holds a prompt but no branch or sandbox. |
| `active` | Active | The conversation is open. A pull request, if there is one, is a draft. |
| `under-review` | Under Review | The pull request is ready for review. A member's message resumes the session and is sent. |
| `completed` | Completed | The pull request merged, or a Backlog session was marked complete. Final. |
| `archived` | Archived | Set aside. Its pull request is closed and its sandbox removed. |

## Read one session

`GET /api/projects/{projectId}/chat-sessions/{sessionId}` returns `session`, `latestChatJob` and `prUrl`. `prUrl` is null until the agent opens a pull request. `session.isBehind` says the branch is behind the Target Branch, and `session.prChecksStatus` is `pending`, `running`, `passed` or `failed`. The CLI command is `kosuke sessions get <projectId> <sessionId>`.

`GET .../chat-sessions/{sessionId}/messages` returns the whole conversation, oldest first. Any member of the workspace can read it.

| Query | What it reads |
| --- | --- |
| None | The whole conversation. |
| `limit` | The newest `limit` messages, up to 500. |
| `before` | The messages older than an `olderCursor` an earlier read returned. With `limit`, the newest `limit` of them. |
| `from` | The message an `olderCursor` names and every message after it. With `limit`, the oldest `limit` of them. |

`before` and `from` together answer `400`, and so does a cursor this endpoint did not return. The answer holds `messages`, `olderCursor` and `sessionInfo`. `olderCursor` names the oldest message returned while older ones exist, and is null once the read reaches the first message. Pass it back as `before` to page back, or as `from` to read the same range again with whatever was added since. `sessionInfo.messageCount` counts the whole conversation, and `sessionInfo.firstPrompt` holds the `id`, `content` and `attachments` of the session's first user message, whichever page you read.

Each block comes summarized. Text, plan, clarification and artifact blocks come whole. A `tool` block keeps the short fields of its `input`, each cut to 200 characters, and the `question` of each of its `questions`, and carries `inputCompacted: true`. A tool call's `result` over 500 characters keeps its first 500 and carries `resultCompacted: true`. A subagent keeps its name, status and last output entry. It carries `outputCount` for how many entries it has and `outputOmitted: true`. An `agentOutput` other than `summary` answers `400`.

`GET .../chat-sessions/{sessionId}/messages/{messageId}` returns one message whole, results included. Its `agentOutput` is `full`, the default, `compact` or `summary`.

| `agentOutput` | What one message returns |
| --- | --- |
| `full` | Every block as recorded: whole inputs, results and subagent output. |
| `compact` | Whole inputs and results. Each subagent keeps its last output entry and `outputCount`. In a message recorded before previews were stored, it keeps every entry, each cut to its short fields. |
| `summary` | The blocks as the list returns them. |

The CLI commands are `kosuke sessions messages list` and `kosuke sessions messages get`.

## Change a chat session

`PUT /api/projects/{projectId}/chat-sessions/{sessionId}` changes the fields you send and leaves the rest.

| Field | Type | When it can change |
| --- | --- | --- |
| `title` | string, 1 to 255 characters | Always. |
| `priority` | `urgent`, `high`, `medium`, `low` or `none` | Any status except Completed and Archived. |
| `model` | a value from `GET /api/models` | In Backlog. In Active, only while no turn runs. |
| `startBranch` | string, or `null` | In Backlog. |
| `sandboxSize`, `appStack` | as on create | In Backlog. Refused with `409 SANDBOX_SETTINGS_LOCKED` once the session has started. |
| `ownerId` | member id, or `null` to unassign | See [Reassign](https://kosuke.ai/docs/endpoint-reference/chat-sessions.md#reassign). |
| `status` | a status value | See the transitions below. |

A model from another provider starts the agent fresh. The next turn opens with the conversation handed over as text.

```bash
curl -s -X PUT "https://app.kosuke.ai/api/projects/$PROJECT_ID/chat-sessions/$SESSION_ID" \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "under-review"}'
```

The answer is `200` with the updated `session`. The CLI takes the same fields as flags, as in `kosuke sessions update <projectId> <sessionId> --status under-review`.

### App stack

A session filed with `appStack: "skip"` boots without the project's services: setup runs and the agent works, and the Preview reads **App stack not started**. The session's `appStack` and `appStackStartedAt` say so: `skip` with a null `appStackStartedAt` waits for a start. A member presses **Start app stack** in the Preview, or anyone who can reach the session, its own sandbox included, sends `POST /api/projects/{projectId}/chat-sessions/{sessionId}/preview/app-stack`, which starts the services and answers once they respond. While a start runs, the session's `appStackState` reads `starting` for every viewer; a start that failed reads `failed`, with the reason in `appStackError`, and `appStackStartedAt` is set only once the services answered. A start while the sandbox's setup still runs answers 409 `APP_STACK_NOT_READY`, and one while another start runs 409 `APP_STACK_STARTING`: retry either in a minute. Another session's sandbox is refused. Once started, the services stay started through every pause and every new sandbox.

```bash
kosuke sessions app-stack start $PROJECT_ID $SESSION_ID
```

A session on a Sandbox size other than its project's boots from that size's base template and runs setup in full. It is billed at the size it runs on.

### Deliver new environment variables

A session sandbox reads the project's environment variables, services and setup commands when it boots. A variable added or changed later reaches it only when a member delivers it: `POST /api/projects/{projectId}/chat-sessions/{sessionId}/env-sync` writes the project's saved settings into that session's sandbox and nowhere else, reruns setup and restarts the services. Once the services answer again it returns `status: "synced"` with the variable names the sandbox now holds, never their values. A session whose App stack waits for a start answers `servicesRestarted: false`: the start reads the new values. When setup runs past ten minutes it returns `status: "running"` and the sync goes on; another sync is refused until it ends.

```bash
kosuke sessions env sync $PROJECT_ID $SESSION_ID
```

It is refused, never queued, while something else owns the sandbox. Run it again once that ends.

### Status transitions

| From | To | What happens |
| --- | --- | --- |
| Backlog | Completed | Marks the work done without a branch. Only for a session that never started. Send `status` alone. |
| Backlog | Archived | Sets the session aside. |
| Active | Under Review | Marks the pull request ready for review. Needs an open pull request. |
| Under Review | Active | Turns the pull request back into a draft. |
| Active or Under Review | Archived | Closes the pull request and removes the sandbox. |
| Archived | Active | Reopens the pull request as a draft. A session that never started answers `400`. Send `backlog` for it instead. |
| Archived | Under Review | Reopens the pull request and marks it ready for review. Needs a pull request. |
| Active, Under Review or Archived | Backlog | Removes the sandbox, closes the pull request, and removes the work and the chat history. The first prompt, its attachments and properties stay. Send `status` alone. |

A session reaches Completed when its pull request merges. From Completed, no status change is accepted. Changes to the pull request go through GitHub first. When GitHub does not accept a reopen, a ready-for-review or a draft change, you get `503` and the session stays where it was. The same holds when GitHub does not close the pull request on a move to Backlog. Archiving goes ahead even when GitHub does not close the pull request.

### Reassign

On a Backlog session, `ownerId` sets the Assignee, and `null` leaves the session unassigned. On a session that has started, `ownerId` reassigns it:

- It works in every status except Completed.
- It names a member. `null` is refused on a started session.
- It goes in a request that does not change `status`.
- The new Assignee has to be able to run the session's model. To change both, send `model` in the same request.

The branch, the conversation and the pull request stay as they are. The new Assignee's Personal Skills load into the sandbox, and their credentials run the next turn. The CLI flag is `--owner-id`.

## Delete a chat session

`DELETE /api/projects/{projectId}/chat-sessions/{sessionId}` answers `204`. The session and its conversation are gone at once. Sensei then closes its pull request and removes its sandbox. The CLI command is `kosuke sessions delete <projectId> <sessionId>`.

## Labels

`PUT /api/projects/{projectId}/chat-sessions/{sessionId}/labels` replaces the labels a session carries. Send up to 20 names, each up to 50 characters, stored lowercase; `[]` removes them all. New names join the project's labels, and `GET /api/projects/{projectId}/labels` lists them. Labels stay in Sensei and never reach the pull request.

```bash
kosuke sessions labels set <projectId> <sessionId> --labels '["bug","ui"]'
```

## Search

`GET /api/chat-sessions/search?q=` finds sessions by title across every workspace your API key reaches. It returns up to 20 matches, latest activity first. An empty `q` returns the 10 most recent sessions. Archived sessions are left out.

```bash
kosuke sessions search --q "dark mode"
```

The answer is `{ "data": { "sessions": [...] } }`, each with its `projectName` and `latestChatJobStatus`.

## Edit a Backlog prompt

`PATCH .../prompt` rewrites the saved prompt of a Backlog session. It takes `multipart/form-data` only.

| Field | What it does |
| --- | --- |
| `content` | Required. The new prompt. |
| `model`, `priority` | Optional. As on create. |
| `ownerId` | Optional. A member id, or an empty string to unassign. |
| `retainedAttachmentIds` | Optional. A JSON array of the attachment ids to keep. The others are removed. |
| `attachmentCount`, `attachment_0`… | Optional. New files, as on create. |

## Up next

A message sent while a turn runs waits Up next, and each one starts its own turn in order. The endpoints under `.../queue` read and change that list. To send a message, see [Messages and turns](https://kosuke.ai/docs/endpoint-reference/messages-and-turns.md).

| Endpoint | Body | Who can use it |
| --- | --- | --- |
| `GET .../chat-sessions/{sessionId}/queue` | None | Anyone in the workspace. Returns the messages in order and whether Up next is paused. |
| `PATCH .../chat-sessions/{sessionId}/queue/{queuedMessageId}` | `content`, and optionally `retainedAttachmentIds`. Multipart for new files. | The message's author. |
| `POST .../chat-sessions/{sessionId}/queue/{queuedMessageId}/move` | `{ "toIndex": 0 }`, counted from the top | The message's author. |
| `POST .../chat-sessions/{sessionId}/queue/{queuedMessageId}/steer` | None | The author, into a turn they started. |
| `POST .../chat-sessions/{sessionId}/queue/resume` | None | Anyone in the workspace. Sends the top message once nothing runs. |
| `DELETE .../chat-sessions/{sessionId}/queue/{queuedMessageId}` | None | The author, or a workspace owner or admin. |

The `PATCH`, `move`, `resume` and `DELETE` routes answer `204`. A steer that no running turn of yours can take gets `409` with `STEER_UNAVAILABLE`, and the message stays Up next. The CLI group is `kosuke sessions queue`.

## Runtime commands

`POST .../chat-sessions/{sessionId}/runtime-commands` runs `/clear` or `/compact` on the agent's memory of the conversation. The messages stay on screen.

| Field | Type | What it does |
| --- | --- | --- |
| `command` | `clear` or `compact` | `clear` starts the agent fresh. `compact` summarizes the conversation to free context. |
| `instructions` | string | Optional. What `compact` should focus on. |

```bash
kosuke sessions runtime-commands run $PROJECT_ID $SESSION_ID --command compact
```

`compact` needs a running sandbox and answers `400` with `Sandbox is not running` otherwise. It answers once the summary is done, which can take a minute. A command sent while a turn or another command runs waits Up next: the answer is `201` with `delivery: "queued"` and its `queuedMessageId`.

## Errors

| Status | Code | When |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | A field is missing or has the wrong type. |
| 400 | `BAD_REQUEST` | The request breaks a rule on this page. For example, the change is not allowed in the session's status, or the Assignee cannot run the model. Creating a session answers `400` when the project was never deployed, and starting a Backlog session answers `400` when the workspace is at its concurrent sandbox limit. The message says which. |
| 402 | `CREDIT_LIMIT_REACHED` | Starting a session while the workspace is blocked on billing. |
| 403 | `FORBIDDEN` | You are not a member of `workspace`, or the Up next message is not yours. |
| 404 | `NOT_FOUND` | The project or session does not exist, or you cannot reach it. |
| 409 | `DEPLOY_IN_PROGRESS` | Starting a session while the project deploys. Backlog sessions are still accepted. |
| 409 | `SESSION_STATUS_CHANGED` | The session changed while your request ran. Read it again and retry. |
| 409 | `SESSION_UPDATING`, `SESSION_BUSY` | Another change to the session is in progress. Retry shortly. |
| 409 | `SESSION_CHANGED` | The session changed while your request ran. Read it again before you retry. |
| 409 | `STEER_UNAVAILABLE` | No running turn of yours can take the steer. |
| 409 | `COMMAND_IN_FLIGHT` | A runtime command is running. The session cannot move to Backlog, and that command cannot be changed or removed from Up next. |
| 409 | `SANDBOX_STARTING` | The sandbox is still starting, so the session cannot move to Backlog or be archived. Retry shortly. |
| 409 | `QUEUE_FULL` | Up next already holds 20 messages. |
| 409 | `SANDBOX_SETTINGS_LOCKED` | A change to `sandboxSize` or `appStack` after the session started. |
| 503 | `SERVICE_UNAVAILABLE` | GitHub did not accept the pull request change. Retry. |

[Responses and errors](https://kosuke.ai/docs/api/errors.md) has the full table.

## Next steps

- [Messages and turns](https://kosuke.ai/docs/endpoint-reference/messages-and-turns.md)
- [Follow a turn](https://kosuke.ai/docs/api/turns.md)
- [Pull requests and sync](https://kosuke.ai/docs/endpoint-reference/pull-requests-and-sync.md)
