Sandboxes and previews
A session's sandbox pauses when nobody uses it. Before you read its files, diff or git state, request the session's preview. That request wakes the sandbox, or creates it. A paused sandbox wakes in the background, so the answer can come back before it is up, with provisioning: true and no URLs.
When you need a running sandbox
| You want to | What to do |
|---|---|
| List files, read the diff or read git state | The read answers without the data. The file list, the changed files and the commit list come back empty, and the patch answers 503. Git state reports available: false. Wake the sandbox first. |
| Read or write one file | Wake the sandbox first. |
| Reset the preview | Wake the sandbox first. The reset restarts services in a running sandbox. |
| Commit or push | Nothing to do. These wake a paused sandbox themselves. |
| Send a message | Nothing to do. Sending wakes a paused sandbox itself, and creates one for a session that has none. The turn starts once the sandbox is up. |
Wake the sandbox
Request the session's preview. Leave out sessionId to ask for the project's main preview instead.
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/preview?sessionId=$SESSION_ID" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
With the CLI, run kosuke preview get <projectId> --session-id <sessionId>.
{
"data": {
"frontendUrls": { "web": "https://…" },
"projectId": "3f1c2a9e-…",
"sandboxKey": "9b2e4f60-…"
}
}
frontendUrls maps each endpoint the project shows in the preview to its URL. The key is the service name, or <service>-<endpoint> for a service's second and later endpoints.
Read the response
Read the flags before the URLs. A refusal about capacity or billing still answers 200.
| Response | Meaning | What to do |
|---|---|---|
frontendUrls has URLs | The sandbox is up. | Wait for its services, then read. |
provisioning: true, empty frontendUrls | The sandbox is already starting. | Poll the health request until state is healthy. |
creditLimitReached: true, empty frontendUrls | The workspace is blocked on billing, so nothing started. | Stop. Retrying fails until an admin sorts out billing under Plans and billing. |
sandboxLimitReached: true, empty frontendUrls | The workspace already runs as many session sandboxes as its plan allows. | Free a slot, or wait for an idle sandbox to pause, then ask again. |
Empty frontendUrls and no flag | The session is in the Backlog, Completed or Archived. Or the main preview is being deployed. Or no service is exposed as a preview. | Read the session again. For a live session, poll the health request. |
400 with an error message | The project has never been deployed, a deploy is under way, the session has no branch yet, or the project's GitHub access needs reconnecting. | Act on the message. Ask again once the deploy ends. |
Wait for the services
The preview request returns when the sandbox is up. Its services may still be starting. Poll the health request until state is healthy:
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/preview/health?sessionId=$SESSION_ID" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"data": {
"state": "healthy",
"frontendUrls": { "web": "https://…" },
"services": [{ "name": "agent", "httpResult": "healthy" }, { "name": "web", "httpResult": "healthy" }],
"sandboxAvailable": true
}
}
state is starting or healthy. paused: true or needsStart: true means nothing is bringing the sandbox up, so request the preview once more. indeterminate: true means this read learned nothing, because a pause or resume held the sandbox or it did not answer. Ignore the rest of the answer and ask again in a few seconds, without requesting the preview. With the CLI, run kosuke preview health <projectId> --session-id <sessionId>.
A sandbox you woke pauses again when it sits idle. Request the preview again before a later read.
Free a slot
Set WORKSPACE_ID to the project's orgId, from GET /api/projects/{projectId}. Then read how many sandboxes may run at once, and which sessions hold them:
curl -s "https://app.kosuke.ai/api/organizations/$WORKSPACE_ID/sandbox-limit" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"data": {
"maxConcurrentSandboxes": 5,
"runningSessions": [
{ "sessionId": "9b2e4f60-…", "title": "Add a healthz endpoint", "projectName": "storefront", "branch": "…" }
]
}
}
maxConcurrentSandboxes is null on a plan with no limit. Pause the sandboxes of up to 50 sessions at once:
curl -s -X POST "https://app.kosuke.ai/api/organizations/$WORKSPACE_ID/stop-sandboxes" \
-H "Authorization: Bearer $KOSUKE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"sessionIds": ["9b2e4f60-…"]}'
The response lists each session with stopped: true or false. A paused sandbox keeps its work and wakes on the next preview request or message. Plans and billing lists each plan's limit.
Reset the preview
A reset restarts the preview's services inside the running sandbox. Wake the sandbox first.
curl -s -X POST "https://app.kosuke.ai/api/projects/$PROJECT_ID/preview/reset" \
-H "Authorization: Bearer $KOSUKE_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"sessionId\": \"$SESSION_ID\"}"
The reset answers with status: "queued" inside data, next to projectId and sandboxKey, and runs in the background. Poll the health request to see the services come back. A reset of a Backlog, Completed or Archived session answers 400.