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=&completedSince=&workspace=&project=&projects=&labels=&ids=&needsYou=&viewer=&status=&owners=&priorities=&q=&createdBy=&includeArchived=&limit=&cursor= | List chat sessions across every workspace you belong to, each with its workspace, project, URL and Needs You reason. q narrows them to titles containing it, ids to the sessions it names. With needsYou, at most 100, most severe first. 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. |
| 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. |
| GET | /api/projects/{projectId}/chat-sessions?status=&labels=&priorities=&search=&limit=&cursor=&sortBy=&sortOrder=&sortByPriority=&excludeArchived=&excludeBacklog=&owners=&createdBy=&includePrompt= | 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. 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. |
| PUT | /api/projects/{projectId}/chat-sessions/{sessionId} | Change title, status, model, Auto, 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, or Batch merging 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 neither the Merge agent nor a Batch merges 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. |
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. 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. |
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} before you send the first message. |
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:
{
"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 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.
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:
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.
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 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.
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. |
needsYou | true keeps the sessions that need a person, most severe first. It returns up to 100 and never pages. |
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, url and needsYouReason.
curl -s "https://app.kosuke.ai/api/chat-sessions?needsYou=true" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"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/…",
"needsYouReason": "awaiting_response"
}
],
"meta": { "pagination": { "pageSize": 100, "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. |
Needs You reasons
A live session, Active or Under Review, that is waiting on a person carries one reason in needsYouReason. The most severe reason wins. These ten values are all the API returns. With Sensei, the app also turns each reason into a need in a Sensei thread, and adds reasons that only threads show, such as a review that asked for changes. Those never appear in needsYouReason. See Decide what needs you.
On GET /api/chat-sessions, each reason is read for the session's Assignee by default. With viewer=me, the list reads Needs You for you instead. Your own sessions and the sessions booked to Sensei carry their reasons, and a pull request that waits on your approval carries awaiting_approval. Other sessions carry no reason. With owners too, the list also keeps the sessions whose pull request waits on your approval.
A session that is still shipping carries no reason, unless it stopped for a person: a failed turn, a question or a /kosuke:ship stop. A session is shipping while a turn runs or waits to start, and between the turns of a /kosuke:ship run.
| Value | What the session waits for |
|---|---|
checks_failed | Its pull request's checks failed. |
turn_failed | Its latest turn failed. |
awaiting_response | The agent asked a question and waits for the answer. |
ship_stop | /kosuke:ship stopped on a prototype or a failure, or on a ready that did not verify. |
ready_to_merge | Its pull request is approved with green checks and waits for its Assignee to merge it. |
review_not_requested | Its pull request is ready for review, or a draft whose work is Ready, and not approved, and nobody was asked to review it and nobody has reviewed it. It waits for its Assignee to ask for a review. |
awaiting_approval | Its pull request is ready for review, or a draft whose work is Ready, and GitHub asked you to review it. It waits on your approval. Only returned with viewer=me. |
conflicts | Its pull request is open and GitHub reports it conflicts with its target branch, on a session /kosuke:ship does not run. Resolve the conflicts on the branch. |
behind | Its pull request is open and its branch is behind the Target Branch, on a session /kosuke:ship does not run. Sync it with gh pr update-branch, or send /kosuke:sync-branch, as Pull requests and sync shows. |
stale_pr | Its pull request is open and the session has had no activity for three days. |
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. |
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.
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.
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.
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.
nullis 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
modelin 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.
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.
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.
| 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. |
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 has the full table.