# Deploys

A Deploy brings up a project's main sandbox. The deploy agent reads the repository, writes the project settings and boots the sandbox. Start one with `POST /api/projects/{projectId}/settings/agentic-deploy`, answer the agent on the same route with `userResponse`, and follow it with `GET /api/projects/{projectId}/deploy-messages`.

Deploy Chat sends and queued-message edits use the same [attachment transport as chat sessions](https://kosuke.ai/docs/endpoint-reference/chat-sessions.md): up to 20 files, each at most 10 MiB (10,485,760 bytes). The CLI stages batches larger than 20 MiB through the operation's advertised project attachment endpoint, preserving the original order, then sends opaque references in the ordinary multipart attachment slots. Smaller batches stay inline.

| Method | Path                                                                              | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| ------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| GET    | `/api/projects/{projectId}/deploy-messages?limit=&before=&from=`                  | Read the deploy conversation, from the user's latest deploy on, oldest first, and the current deploy status: 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. Blocks come as stored. In a message recorded since previews were 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 deploys messages get` reads one message whole. |
| GET    | `/api/projects/{projectId}/deploy-messages/{messageId}?agentOutput=`              | Read one message of the deploy conversation 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).                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| GET    | `/api/projects/{projectId}/deploys?page=&pageSize=`                               | List the project's Deploys, newest first: whether a member (user) or the platform (system) started each, its status, error and timestamps, with the total in meta.pagination.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy`                               | Start a deploy (empty JSON body) or continue the current one with userResponse. While the deploy agent works, the message waits Up next; while the sandbox builds, it joins the agent's first turn as instructions. Attachments ride along as multipart, where the message is called content. Only a workspace admin, in the browser: an API key, a connected app and every agent token get 403. A started deploy is recorded in Change history.                                                                                                                                                                                                                                           |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy/cancel`                        | Stop the running deploy. Steers its agent had not taken go back to the top of Up next, which pauses; instructions sent while its sandbox built are discarded and listed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| PUT    | `/api/projects/{projectId}/settings/agentic-deploy/model`                         | Pick the model the deploy agent runs on, as an encoded id such as anthropic:sonnet__medium. Refused while a deploy runs or waits for an answer.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| GET    | `/api/projects/{projectId}/settings/agentic-deploy/queue`                         | List the messages waiting Up next in the Deploy Chat, in the order they will be sent, and whether Up next is paused.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| PATCH  | `/api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}`       | Save an edit to your own queued Deploy Chat message, keeping its place in Up next. New files ride along as multipart. Only a workspace admin, in the browser: an API key, a connected app and every agent token get 403.                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| DELETE | `/api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}`       | Delete a queued Deploy Chat message: your own, or any as a workspace owner or admin.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}/move`  | Move your own queued Deploy Chat message to another place in Up next.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}/send`  | Send your own queued Deploy Chat message now, ahead of the rest of Up next. Only while the deploy agent is idle; it resumes a paused Up next. Only a workspace admin, in the browser: an API key, a connected app and every agent token get 403.                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}/steer` | Send your own queued Deploy Chat message into the running deploy agent Turn as a steer, on any deploy model whose runtime takes one; the message stays queued when nothing can take it. Only a workspace admin, in the browser: an API key, a connected app and every agent token get 403.                                                                                                                                                                                                                                                                                                                                                                                                 |
| POST   | `/api/projects/{projectId}/settings/agentic-deploy/queue/resume`                  | Resume the Deploy Chat's paused Up next. Its top message is sent as soon as the deploy is idle. Only a workspace admin, in the browser: an API key, a connected app and every agent token get 403.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

Any member of the workspace can read these routes. Only a workspace admin, in the web app, starts a Deploy or sends to one (`POST /api/projects/{projectId}/settings/agentic-deploy`, and a queued message's send): an API key, a Connected App and every agent get 403 `SETTINGS_ADMIN_ONLY` until keys carry a project settings scope. A workspace Deploy uses workspace Credentials only. A personal-project Deploy uses the owner's Subscriptions, or Sensei free when they hold none. What a Deploy does in the web app, and what to do when one fails, is on [Deploy your project](https://kosuke.ai/docs/set-up/deploy.md).

## Start a Deploy

Creating a project starts its first Deploy, so a new project needs no request here. See [Projects](https://kosuke.ai/docs/endpoint-reference/projects.md#create-a-project). If that Deploy fails, Sensei starts one Recovery Deploy of it.

Send the request with an empty body. It starts a new Deploy and stops any Deploy already running. The web app's Redeploy sends the same request, but only when nothing runs or the agent is waiting on a Clarification.

```bash
curl -s -X POST https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": {
    "delivery": "job",
    "status": "creating_sandbox",
    "deployJobId": "c7e2a9d4-5b1f-4e8a-9c3d-2f6b0a1e4d87"
  }
}
```

To give a project's first Deploy instructions, send them with `freshDeploy`. This starts a new Deploy only on a project that has never deployed.

| Body field           | Meaning                                                                                     |
| -------------------- | ------------------------------------------------------------------------------------------- |
| `userResponse`       | Your message to the deploy agent.                                                           |
| `freshDeploy`        | `true` starts the first Deploy with `userResponse` as its instructions.                     |
| `runningDeployJobId` | The `deployJobId` you saw running. See [Answer the deploy agent](https://kosuke.ai/docs/endpoint-reference/deploys.md#answer-the-deploy-agent). |

```bash
curl -s https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  --json '{ "userResponse": "The API lives in apps/api and needs Redis.", "freshDeploy": true }'
```

To attach files, send the request as `multipart/form-data`. The message goes in a `content` field next to the files. A multipart request always needs `content`.

When nothing can run the deploy model, the Deploy starts and then fails. In a workspace project its `error` reads "The workspace has no usable credentials for the Deploy model. Ask a workspace admin to reconnect its provider in Workspace AI Providers or change the Deploy model setting." A Deploy nobody pressed, such as an import's first Deploy, is skipped instead, and its `error` starts with "Deploy skipped:". A workspace Deploy never runs on a member's Subscription or on Sensei free. In a personal project the Deploy runs on the owner's chosen Deploy model, or else on the first connected provider in catalog order and its catalog default model, and fails with "The owner's selected Deploy model cannot run on their personal subscriptions." when that choice cannot run: limits, reconnects and failures never move it to Free. Connecting the first Subscription moves a Free Deploy to the provider's catalog default model from its next turn; removing the last moves its choice to Free. A running turn keeps its source.

A Free Deploy works like a personal Subscription, within the Free allowance and staff maximum Turn Level. Sensei picks one Source for the Deploy and moves it automatically if that Source reaches its limit, is paused or is removed. Its model, Source and provider stay hidden.

In the CLI, run `kosuke deploys start <projectId>`. Add `--user-response "..." --fresh-deploy` for instructions. To attach files, pass the message as `--content "..."` and add `--attach <path>` for each file.

## Follow a Deploy

Read the Deploy Chat and the current state.

```bash
curl -s https://app.kosuke.ai/api/projects/$PROJECT_ID/deploy-messages \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": {
    "status": "running_agent",
    "deployJobId": "c7e2a9d4-5b1f-4e8a-9c3d-2f6b0a1e4d87",
    "activeStreamId": "d9f1b3c5-7e2a-4d6b-8a0c-1e3f5a7b9c2d",
    "lastStreamId": "d9f1b3c5-7e2a-4d6b-8a0c-1e3f5a7b9c2d",
    "error": null,
    "deploySandboxId": "i8k2m4p6r9t1v3x5z7b0d",
    "deployModel": "anthropic:opus__xhigh",
    "credentialOwners": {
      "anthropic": { "kind": "organization", "needsReauth": false, "authMethod": "api_key" },
      "openai": null,
      "zai": null,
      "moonshot": null,
      "deepseek": null
    },
    "messages": [
      {
        "id": "0e4c8a2f-6b1d-4f3e-9a5c-7d2b1e0f8a36",
        "deployJobId": "c7e2a9d4-5b1f-4e8a-9c3d-2f6b0a1e4d87",
        "role": "user",
        "content": "deploy my project",
        "attachments": [],
        "author": {
          "userId": "u8Rk2mQ4xT7vB1nP9sL3cW6yD0fH5jZa",
          "name": "Sam Rivera",
          "email": "sam@acme.com",
          "imageUrl": null
        },
        "createdAt": "2026-09-30T10:02:11.000Z"
      }
    ]
  }
}
```

`messages` starts at the project's most recent Deploy a member started, and includes the Recovery Deploys Sensei started after it. `role` is `user`, `assistant` or `system`.

Each message's `blocks` come as stored. A message recorded since previews were stored has them cut the way a chat session's messages are, as [Read one session](https://kosuke.ai/docs/endpoint-reference/chat-sessions.md#read-one-session) describes. There, `inputCompacted` and `resultCompacted` mark only an input or result that was cut. `outputOmitted` marks only a subagent with more than one entry, or a long one. `GET /api/projects/{projectId}/deploy-messages/{messageId}` returns one message with every block as recorded, results whole. Its `agentOutput` is `full` by default. `compact` returns whole inputs and results, with subagent output as the list returns it, and `summary` the blocks as stored. In the CLI, run `kosuke deploys messages get <projectId> <messageId>`.

| `status`            | Meaning                                                                                                                                                |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `idle`              | The project has no Deploy yet.                                                                                                                         |
| `creating_sandbox`  | Sensei is creating a fresh sandbox.                                                                                                                    |
| `starting_services` | The services are starting, and Sensei waits for them to be healthy.                                                                                    |
| `running_agent`     | The deploy agent is setting up and checking the project.                                                                                               |
| `updating`          | The deploy agent is working on the live project.                                                                                                       |
| `awaiting_user`     | The deploy agent asked a Clarification. Answer it with `userResponse`.                                                                                 |
| `warming`           | Sensei loads pages so the sandbox starts with a warm cache.                                                                                            |
| `promoting`         | Sensei runs final checks and makes the new sandbox live.                                                                                               |
| `warming_pool`      | Sensei prepares spare sandboxes so new tasks start fast.                                                                                               |
| `deployed`          | The project is live.                                                                                                                                   |
| `failed`            | The Deploy failed. `error` says why.                                                                                                                   |
| `cancelled`         | The Deploy was stopped: by a member, by a newer Deploy that replaced it, or by Sensei when it could not run. When Sensei stopped it, `error` says why. |

To stream the running turn instead of polling, pass `activeStreamId` to the CLI. The session id argument is not read for a Deploy, so any value works there:

```sh
kosuke sessions jobs watch <projectId> _ <activeStreamId> --type deploy
```

How to poll well is on [Follow a turn](https://kosuke.ai/docs/api/turns.md). In the CLI, the read above is `kosuke deploys messages list <projectId>`.

## Answer the deploy agent

When `status` is `awaiting_user`, the agent is waiting for you. Send your answer as `userResponse`.

```bash
curl -s https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  --json '{
    "userResponse": "Use a Stripe test key. I added STRIPE_SECRET_KEY as a secret.",
    "runningDeployJobId": "c7e2a9d4-5b1f-4e8a-9c3d-2f6b0a1e4d87"
  }'
```

> **Warning.** Always send the message as `userResponse`. A JSON body without `userResponse` starts a new Deploy and stops the running one. That includes a body that carries the message in `content`, which only multipart requests read.

Pass the `deployJobId` you saw as `runningDeployJobId`. If someone stopped that Deploy before your message arrived, the call answers `409 JOB_STOPPED` instead of starting over.

The same route takes any message for the deploy agent. What happens depends on where the Deploy is:

| When you send                                                                                           | What happens                                              | Answer                                               |
| ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ---------------------------------------------------- |
| The agent is working (`running_agent` or `updating`)                                                    | The message waits Up next and starts the next turn.       | `201`, `delivery: "queued"` with a `queuedMessageId` |
| The sandbox is still building (`creating_sandbox`, `starting_services`, `warming` or `promoting`)       | The message joins the agent's first turn as instructions. | `delivery: "steer"`                                  |
| The agent asked a Clarification (`awaiting_user`)                                                       | Your answer continues the Deploy. It may carry files.     | `delivery: "job"`                                    |
| The project is deployed and nothing runs                                                                | The agent starts a new turn on the live project.          | `delivery: "job"`, `status: "updating"`              |
| You stopped the project's first Deploy                                                                  | The Deploy resumes where it stopped.                      | `delivery: "job"`                                    |
| The project has no main sandbox (it never deployed, or its main sandbox was reclaimed) and nothing runs | Refused with `409 NOT_DEPLOYED`. Start a Deploy first.    |                                                      |

In the CLI, run `kosuke deploys start <projectId> --user-response "..." --running-deploy-job-id <deployJobId>`.

## Stop a Deploy

Stop the running Deploy.

```bash
curl -s -X POST https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy/cancel \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{ "data": { "cancelled": 1, "discardedInstructionIds": [], "discardedInstructions": [] } }
```

After a stop, messages you steered that the agent had not taken go back to the top of Up next, and Up next pauses. Instructions sent while the sandbox was building are dropped and returned in `discardedInstructions`. A stopped first Deploy keeps its sandbox, so your next message resumes it. With nothing running, `cancelled` is `0`.

In the CLI, run `kosuke deploys cancel <projectId>`.

## Pick the deploy model

Choose the model the deploy agent runs on. Take a `value` from [Models](https://kosuke.ai/docs/endpoint-reference/models.md).

| Body field | Meaning                                                                                                         |
| ---------- | --------------------------------------------------------------------------------------------------------------- |
| `model`    | A model of any provider, such as `openai:gpt-6.1-sol__high`. The workspace needs Credentials for that provider. |

```bash
curl -s -X PUT https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy/model \
  -H "Authorization: Bearer $KOSUKE_API_KEY" \
  --json '{ "model": "openai:gpt-6.1-sol__high" }'
```

```json
{ "data": { "deployModel": "openai:gpt-6.1-sol__high" } }
```

Change the model between Deploys. While a Deploy runs or waits for an answer, the route answers `400`. Switching to another provider starts the agent's next turn fresh, with the conversation so far handed over as text. When no model is saved, the deploy agent runs on `anthropic:opus__xhigh`. Without Anthropic it runs on `openai:gpt-6-astra__high`, then on `zai:glm-5.3__high`, `moonshot:k3__high` and `deepseek:deepseek-flash__high`, in that order. `deployModel` in the Deploy Chat response shows the current one. If the workspace loses the Credentials for the saved model's provider, pick another model before the next Deploy.

In the CLI, run `kosuke deploys model set <projectId> --model openai:gpt-6.1-sol__high`.

## Up next

A message sent while the deploy agent works waits Up next, as in a chat session. Up next holds at most 20 messages. It pauses when a Deploy fails, is stopped, or ends on a Clarification.

```bash
curl -s https://app.kosuke.ai/api/projects/$PROJECT_ID/settings/agentic-deploy/queue \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": {
    "messages": [
      {
        "id": "4b7d1f3a-9e2c-4a5b-8d6f-0c1e2a3b4c5d",
        "content": "Also seed the database with the demo fixtures.",
        "position": 0,
        "author": {
          "userId": "u8Rk2mQ4xT7vB1nP9sL3cW6yD0fH5jZa",
          "name": "Sam Rivera",
          "email": "sam@acme.com",
          "imageUrl": null
        },
        "attachments": [],
        "context": {},
        "editingBy": null,
        "can": { "edit": true, "move": true, "steer": true, "delete": true },
        "createdAt": "2026-09-30T10:06:40.000Z"
      }
    ],
    "paused": null
  }
}
```

`can` says what you may do with each message. `paused` is `null`, or holds the `reason` and `pausedAt`.

| Route                                                                                  | What it does                                                                                                                                                          | Who                                       |
| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `PATCH /api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}`      | Saves new `content` and keeps its place. List the files to keep in `retainedAttachmentIds`. Answers `204`.                                                            | Its author                                |
| `DELETE /api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}`     | Deletes the message. Answers `204`.                                                                                                                                   | Its author, or a workspace owner or admin |
| `POST /api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}/move`  | Moves it to `toIndex`, counted from the top. Answers `204`.                                                                                                           | Its author                                |
| `POST /api/projects/{projectId}/settings/agentic-deploy/queue/{queuedMessageId}/steer` | Sends it into the running turn as a steer, on any deploy model whose runtime takes one.                                                                               | Its author                                |
| `POST /api/projects/{projectId}/settings/agentic-deploy/queue/resume`                  | Resumes a paused Up next. Its top message goes as soon as no Deploy is running, or at once as the answer when the agent is waiting on a Clarification. Answers `204`. | Any member                                |

A steer the running turn cannot take answers `409 STEER_UNAVAILABLE`, and the message stays Up next.

In the CLI, the commands are `kosuke deploys queue list`, `update`, `delete`, `move`, `send`, `steer` and `resume`.

## Read past Deploys

List the project's Deploys, newest first.

| Query parameter | Meaning                                                         |
| --------------- | --------------------------------------------------------------- |
| `page`          | Page number, from 1. Default 1.                                 |
| `pageSize`      | Deploys per page. Default 25. Values above 100 are read as 100. |

```bash
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/deploys?pageSize=10" \
  -H "Authorization: Bearer $KOSUKE_API_KEY"
```

```json
{
  "data": [
    {
      "id": "c7e2a9d4-5b1f-4e8a-9c3d-2f6b0a1e4d87",
      "trigger": "user",
      "status": "deployed",
      "error": null,
      "createdAt": "2026-09-30T10:02:10.000Z",
      "startedAt": "2026-09-30T10:02:12.000Z",
      "completedAt": "2026-09-30T10:09:51.000Z"
    }
  ],
  "meta": {
    "pagination": { "page": 1, "totalPages": 3, "pageSize": 10, "total": 24, "hasMore": true }
  }
}
```

`trigger` is `user` for a Deploy a member started, the first Deploy an import starts included, and `system` for a Recovery Deploy. `status` takes the values in [Follow a Deploy](https://kosuke.ai/docs/endpoint-reference/deploys.md#follow-a-deploy).

In the CLI, run `kosuke deploys list <projectId> --page-size 10`.

## Errors

| Status | Code                        | When                                                                                                                 |
| ------ | --------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| 400    | `BAD_REQUEST`               | The message is empty, a file is refused, or the model is refused. A model change while a Deploy runs is refused too. |
| 402    | `CREDIT_LIMIT_REACHED`      | The workspace is blocked on billing.                                                                                 |
| 403    | `FORBIDDEN`                 | You changed a queued message you did not write.                                                                      |
| 404    | `NOT_FOUND`                 | No such project or queued message.                                                                                   |
| 409    | `DEPLOY_ACTIVE`             | A Deploy is already running, or still stopping.                                                                      |
| 409    | `JOB_STOPPED`               | The Deploy you named in `runningDeployJobId` was stopped.                                                            |
| 409    | `NOT_DEPLOYED`              | The project has no main sandbox, because it never deployed or its main sandbox was reclaimed. Start a Deploy.        |
| 409    | `DEPLOY_RESUME_UNAVAILABLE` | The stopped Deploy cannot resume. Start a new one.                                                                   |
| 409    | `QUEUE_FULL`                | Up next already holds 20 messages.                                                                                   |
| 409    | `STEER_UNAVAILABLE`         | No turn can take the steer right now. The message stays Up next.                                                     |
| 409    | `JOB_STOPPED_TEXT_RETURNED` | The turn you steered into was stopped before it took the message. The message stays Up next.                         |
| 409    | `QUEUED_MESSAGE_CHANGED`    | The message changed or was sent while you edited it. Read Up next again before you save.                             |
| 409    | `QUEUED_MESSAGE_GONE`       | The message already left Up next. It was sent or deleted.                                                            |

The full list is in [Responses and errors](https://kosuke.ai/docs/api/errors.md).

## Next steps

- Change what the agent wrote with [Project settings](https://kosuke.ai/docs/endpoint-reference/project-settings.md).
- Learn how Deploys work in the web app on [Deploy your project](https://kosuke.ai/docs/set-up/deploy.md).
