Previews
GET /api/projects/{projectId}/preview returns a preview's URLs and wakes its sandbox when it is paused. Pass sessionId for a chat session's preview, and leave it out for the project's main preview. Call it before you read a session's files, diff or git state. From here you can also check the preview's health, restart its services and share it with a public link.
| Method | Path | Description |
|---|---|---|
| GET | /api/projects/{projectId}/preview?sessionId= | Read the preview URLs of the project's main sandbox, or of one chat session's. |
| GET | /api/projects/{projectId}/preview/health?sessionId= | Read the health of every service behind a preview. |
| POST | /api/projects/{projectId}/preview/reset | Restart the services of a preview's running sandbox in place. Fails when the sandbox is paused. |
| GET | /api/projects/{projectId}/preview/share?sessionId= | Read the public share link of a preview, if one exists. |
| POST | /api/projects/{projectId}/preview/share?sessionId= | Create the public share link of a preview. The body sessionId wins over the query one. |
| DELETE | /api/projects/{projectId}/preview/share?sessionId= | Revoke the public share link of a preview. |
| GET | /api/shared-previews/{slug} | Resolve a public share link. No credential needed; rate limited per caller. |
| GET | /api/shared-previews/{slug}/artifacts/{artifactId} | Read one artifact behind a public share link. No credential needed. |
Get the preview and wake the sandbox
| Parameter | What it does |
|---|---|
sessionId | Optional. The chat session whose preview you want. Left out, the call reads the project's main preview. |
A paused sandbox wakes in the background. The call returns provisioning: true with an empty frontendUrls within two seconds, so check health until the services are up. The call waits only while a missing chat session sandbox is created. A missing main sandbox is rebuilt by a deploy, and the call returns before that deploy finishes. Sandboxes and previews explains when a sandbox sleeps and what waking it costs.
curl -s "https://app.kosuke.ai/api/projects/$PROJECT_ID/preview?sessionId=$SESSION_ID" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"data": {
"frontendUrls": { "web": "https://…" },
"projectId": "5a1e9c47-83b2-4d6f-a0e8-2b7c9d14f36a",
"sandboxKey": "c3f8a215-6e0d-4b97-9a42-81d5e7f02c6b",
"awayHoldMs": 600000
}
}
frontendUrls maps each service that serves the preview to its URL. When nothing could start, the answer is still 200, with an empty frontendUrls and one of these flags:
| Flag | What it means | What to do |
|---|---|---|
creditLimitReached: true | The workspace is blocked on billing, so nothing started. | Sort out the workspace's billing. Retrying does not help. |
sandboxLimitReached: true | The workspace already runs as many chat session sandboxes as its plan allows. | Stop another session's sandbox, or wait for an idle one to pause. |
provisioning: true | This call started a wake, or another request is waking, creating or changing this sandbox. | Check health until the services are up. |
An empty frontendUrls with no flag has four readings. The session may be Backlog, Completed or Archived. A deploy may be rebuilding the project's main preview. No service in the project may serve a preview. Or another operation held the session's sandbox for over five seconds, so call again. When the project's main sandbox is gone, asking for the main preview starts that deploy.
The CLI command is kosuke preview get <projectId> --session-id <sessionId>.
Check health
GET .../preview/health reports whether the services behind a preview answer. It takes the same sessionId and never wakes a sandbox.
{
"data": {
"state": "healthy",
"frontendUrls": { "web": "https://…" },
"services": [
{ "name": "agent", "httpResult": "healthy" },
{ "name": "web", "httpResult": "healthy" }
],
"sandboxAvailable": true,
"sandboxId": "i8k2m4q6r1t3v5x7z9b0d",
"canRetainPreview": false
}
}
| Field | What it holds |
|---|---|
state | healthy when every service answers, starting otherwise. |
services | Each probed endpoint's name, which is its route key, and httpResult, which is healthy, not_healthy or null. When the check reached a running sandbox, the list starts with agent, the agent inside the sandbox. A project with public endpoints but none that serves a preview lists only those endpoints, each with a null httpResult. Otherwise the list is empty. |
sandboxAvailable | true when the check reached a running sandbox. |
sandboxId | The sandbox the check reached. Present only when it reached a running sandbox. |
canRetainPreview | true when that sandbox was created with auto-resume off, so a request to its preview does not wake it once it pauses. Present with sandboxId. |
paused | true when the sandbox is paused. Wake it with GET /api/projects/{projectId}/preview. |
needsStart | true when nothing is running or starting. Wake it with GET /api/projects/{projectId}/preview. |
creditLimitReached, sandboxLimitReached | As on GET /api/projects/{projectId}/preview. |
indeterminate | true when the check could not read the sandbox this time. A pause or resume held it, or it did not answer. Ignore the rest of the answer and ask again in a few seconds. Do not wake it with GET /api/projects/{projectId}/preview. |
A Backlog, Completed or Archived session answers 404. The CLI command is kosuke preview health <projectId> --session-id <sessionId>.
Restart the preview's services
POST .../preview/reset restarts the services in a running sandbox, in place. It keeps the files and the branch.
| Field | Type | What it does |
|---|---|---|
sessionId | string | Optional. The chat session whose preview to restart. Left out, the project's main preview restarts. The body itself is required, so send {} for the main preview. |
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 answer comes back at once with { "status": "queued" }, plus the projectId and the sandboxKey. Health reports starting until the restart finishes. A second reset while one runs does not queue another. The restart fails on a paused sandbox, so wake it with GET /api/projects/{projectId}/preview first. A Backlog, Completed or Archived session answers 400. The CLI command is kosuke preview reset <projectId> --session-id <sessionId>.
Share a preview
A share link lets anyone open a preview without a Kosuke account. For a chat session, it also opens the session's artifacts. Any member of the workspace can create or revoke one. GET, POST and DELETE on .../preview/share take the same sessionId, and POST also takes it in the body.
| Call | What it does | Answer |
|---|---|---|
POST .../preview/share | Creates the link, or returns the one that already exists. | { "share": { ... } } |
GET .../preview/share | Reads the link. | { "share": { ... } }, or { "share": null } when there is none |
DELETE .../preview/share | Revokes the link. It stops resolving at once. A page already open keeps the preview until its Auth Token expires, within 15 minutes. | { "share": null }, also when there was no link |
curl -s -X POST "https://app.kosuke.ai/api/projects/$PROJECT_ID/preview/share?sessionId=$SESSION_ID" \
-H "Authorization: Bearer $KOSUKE_API_KEY"
{
"data": {
"share": {
"slug": "Tq3xV9mZkP2rLw7sHc4nB8aQ",
"url": "https://app.kosuke.ai/share/Tq3xV9mZkP2rLw7sHc4nB8aQ",
"createdAt": "2026-09-30T09:20:11.402Z",
"lastViewedAt": null
}
}
}
lastViewedAt is when someone last opened the link, accurate to about five minutes. Opening the link wakes a paused sandbox, and the time it runs counts against the workspace. The CLI group is kosuke preview share, with create, get and delete.
Public share-link endpoints
These endpoints need no API key. Anyone who holds the link can call them. An unknown or revoked link answers 404.
Resolve a link
GET /api/shared-previews/{slug} returns what the link shows and wakes a paused preview.
curl -s "https://app.kosuke.ai/api/shared-previews/$SLUG"
{
"data": {
"projectName": "storefront",
"sessionTitle": "Add a dark mode toggle to the settings page",
"status": "ready",
"frontends": [{ "name": "web", "url": "https://…", "isServing": true }],
"artifacts": [{ "id": "e2c7…", "slug": "qa-report", "title": "QA Report: kosuke/chat-4kd9wp", "kind": "html", "version": 3, "createdAt": "2026-09-30T09:31:47.208Z", "updatedAt": "2026-09-30T10:02:15.664Z" }]
}
}
sessionTitle is null for the main preview. artifacts lists the session's artifacts without their content, whatever status says. It is empty for the main preview.
status | reason | What it means |
|---|---|---|
ready | None | At least one service answers. |
starting | None | The preview is waking or its services are starting. Call again in a few seconds. |
unavailable | session_ended | The session is Backlog, Completed or Archived. |
unavailable | not_running | The preview cannot start now. The workspace may be at its credit or sandbox limit, or the project's main sandbox is gone. |
unavailable | no_frontends | The sandbox runs, but no service serves a preview. |
Read an artifact
GET /api/shared-previews/{slug}/artifacts/{artifactId} returns one artifact of the shared session, in the same shape as on Artifacts. It works while the sandbox is paused. An artifact from another session, or any artifact through a main-preview link, answers 404.
The CLI reads both without an API key, with kosuke preview shared <slug> and kosuke preview shared artifact <slug> <artifactId>.
Rate limits
| Endpoint | Limit per caller |
|---|---|
GET /api/shared-previews/{slug} | 60 requests a minute |
GET /api/shared-previews/{slug}/artifacts/{artifactId} | 30 requests a minute |
Past the limit, the answer is 429 with TOO_MANY_REQUESTS.
Errors
| Status | Code | When |
|---|---|---|
| 400 | BAD_REQUEST | The project was never deployed, a chat session's preview is asked for while a deploy is in progress, or a reset names a Backlog, Completed or Archived session. |
| 400 | VALIDATION_ERROR | A reset body has the wrong shape. |
| 404 | NOT_FOUND | The project, session or share link does not exist, or health was asked for a session with no preview. |
| 429 | TOO_MANY_REQUESTS | A public share-link endpoint passed its rate limit. |
Responses and errors has the full table.