Skip to main content

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.

MethodPathDescription
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}/attachmentsStage 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-sessionsStart 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}/activateStart 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}/desktopRead 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}/desktopStart 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/controlHand 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-syncDeliver 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-aheadGive 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/clearTake 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}/labelsReplace 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}/labelsAdd 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-stackStart 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}/promptRewrite 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-passedRecord 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-runsStart 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}/blockBlock 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}/finishFinish 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}/fixesRecord 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}/resultsApply 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}/queueList 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}/moveMove your own queued message to another place in Up next.
POST/api/projects/{projectId}/chat-sessions/{sessionId}/queue/{queuedMessageId}/sendSend 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}/steerSend 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/resumeResume a session's paused Up next. Its top message is sent as soon as nothing is running.
POST/api/projects/{projectId}/chat-sessions/{sessionId}/readMark 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}/reviewRecord 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-commandsRun 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}/steerSend 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.

FieldTypeRequiredWhat it does
contentstringWith status: "backlog"The first message. Without status, it starts the first turn.
titlestring, 1 to 255 charactersWhen content is absentThe session's title. Left out, Sensei writes one from content.
status"backlog"NoSaves the session to the Backlog instead of starting it. No other value is accepted.
modelstringNoA value from GET /api/models. Left out, the session uses its Assignee's Default Model.
ownerIdstringNoBacklog only. The member id of the Assignee. Left out, a Backlog session is unassigned. A session that starts now is assigned to you.
priorityurgent, high, medium, low or noneNoTriage priority. Default none.
startBranchstringNoThe branch the session starts from, instead of the project's base branch. It must exist on GitHub.
sandboxSizesmall, medium, large or xlargeNoThe 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.
appStackstart or skipNostart, 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 sendResult
contentAn 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 aloneAn 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):

ParameterWhat it does
statusComma-separated status values, such as active,under-review.
prioritiesComma-separated priorities.
ownersComma-separated member ids of Assignees, plus me, none (unassigned) and any.
labelsA JSON array of Session Labels. A session matches when it carries any of them.
searchKeeps sessions whose title contains the text, ignoring case.
createdByagent keeps the sessions Sensei filed. member keeps the rest.
excludeArchivedtrue drops Archived sessions. Ignored when status is set.
excludeBacklogtrue drops Backlog sessions.
sortByactivity orders by latest activity, the default. created orders by creation time. priority orders by priority, then creation time.
sortOrderasc 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.
sortByPrioritytrue is the same as sortBy=priority. Kept for older clients; a 400 beside any other sortBy.
limitPage size, up to 100. Without it, the whole list comes back in one response.
cursormeta.pagination.nextCursor from the previous page. Needs limit and the same sortBy and sortOrder.

Parameters across workspaces (kosuke sessions list):

ParameterWhat it does
workspaceOne workspace id. You get 403 if you are not a member of it.
projectOne project id.
idsUp to 100 comma-separated session ids.
needsYoutrue keeps the sessions that need a person, most severe first. It returns up to 100 and never pages.
status, owners, priorities, createdByAs in the project list.
labelsJSON 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.
qKeeps sessions whose title contains the text, ignoring case.
includeArchivedtrue keeps Archived sessions, which are left out by default. Ignored when status is set.
limitPage size. Default 50, up to 100.
cursormeta.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​

ValueIn the appWhat it means
backlogBacklogSaved and not started. It holds a prompt but no branch or sandbox.
activeActiveThe conversation is open. A pull request, if there is one, is a draft.
under-reviewUnder ReviewThe pull request is ready for review. A member's message resumes the session and is sent.
completedCompletedThe pull request merged, or a Backlog session was marked complete. Final.
archivedArchivedSet 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.

ValueWhat the session waits for
checks_failedIts pull request's checks failed.
turn_failedIts latest turn failed.
awaiting_responseThe 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_mergeIts pull request is approved with green checks and waits for its Assignee to merge it.
review_not_requestedIts 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_approvalIts 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.
conflictsIts 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.
behindIts 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_prIts 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.

QueryWhat it reads
NoneThe whole conversation.
limitThe newest limit messages, up to 500.
beforeThe messages older than an olderCursor an earlier read returned. With limit, the newest limit of them.
fromThe 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.

agentOutputWhat one message returns
fullEvery block as recorded: whole inputs, results and subagent output.
compactWhole 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.
summaryThe 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.

FieldTypeWhen it can change
titlestring, 1 to 255 charactersAlways.
priorityurgent, high, medium, low or noneAny status except Completed and Archived.
modela value from GET /api/modelsIn Backlog. In Active, only while no turn runs.
startBranchstring, or nullIn Backlog.
sandboxSize, appStackas on createIn Backlog. Refused with 409 SANDBOX_SETTINGS_LOCKED once the session has started.
ownerIdmember id, or null to unassignSee Reassign.
statusa status valueSee 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​

FromToWhat happens
BacklogCompletedMarks the work done without a branch. Only for a session that never started. Send status alone.
BacklogArchivedSets the session aside.
ActiveUnder ReviewMarks the pull request ready for review. Needs an open pull request.
Under ReviewActiveTurns the pull request back into a draft.
Active or Under ReviewArchivedCloses the pull request and removes the sandbox.
ArchivedActiveReopens the pull request as a draft. A session that never started answers 400. Send backlog for it instead.
ArchivedUnder ReviewReopens the pull request and marks it ready for review. Needs a pull request.
Active, Under Review or ArchivedBacklogRemoves 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.

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

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.

FieldWhat it does
contentRequired. The new prompt.
model, priorityOptional. As on create.
ownerIdOptional. A member id, or an empty string to unassign.
retainedAttachmentIdsOptional. 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.

EndpointBodyWho can use it
GET .../chat-sessions/{sessionId}/queueNoneAnyone 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 topThe message's author.
POST .../chat-sessions/{sessionId}/queue/{queuedMessageId}/steerNoneThe author, into a turn they started.
POST .../chat-sessions/{sessionId}/queue/resumeNoneAnyone in the workspace. Sends the top message once nothing runs.
DELETE .../chat-sessions/{sessionId}/queue/{queuedMessageId}NoneThe 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.

FieldTypeWhat it does
commandclear or compactclear starts the agent fresh. compact summarizes the conversation to free context.
instructionsstringOptional. 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​

StatusCodeWhen
400VALIDATION_ERRORA field is missing or has the wrong type.
400BAD_REQUESTThe 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.
402CREDIT_LIMIT_REACHEDStarting a session while the workspace is blocked on billing.
403FORBIDDENYou are not a member of workspace, or the Up next message is not yours.
404NOT_FOUNDThe project or session does not exist, or you cannot reach it.
409DEPLOY_IN_PROGRESSStarting a session while the project deploys. Backlog sessions are still accepted.
409SESSION_STATUS_CHANGEDThe session changed while your request ran. Read it again and retry.
409SESSION_UPDATING, SESSION_BUSYAnother change to the session is in progress. Retry shortly.
409SESSION_CHANGEDThe session changed while your request ran. Read it again before you retry.
409STEER_UNAVAILABLENo running turn of yours can take the steer.
409COMMAND_IN_FLIGHTA runtime command is running. The session cannot move to Backlog, and that command cannot be changed or removed from Up next.
409SANDBOX_STARTINGThe sandbox is still starting, so the session cannot move to Backlog or be archived. Retry shortly.
409QUEUE_FULLUp next already holds 20 messages.
409SANDBOX_SETTINGS_LOCKEDA change to sandboxSize or appStack after the session started.
503SERVICE_UNAVAILABLEGitHub did not accept the pull request change. Retry.

Responses and errors has the full table.

Next steps​