CrowNest
API reference

Commands

Run processes in a sandbox synchronously or in the background, cancel them, and read or stream their logs.

A command is a top-level process invocation record. You can run a command and wait for it to exit, or start it in the background and follow its logs by polling or over a server-sent events (SSE) stream.

The Command object

FieldTypeDescription
idstringCommand ID, prefixed cmd_
sandboxIdstringSandbox the command ran in, prefixed sbx_
commandstringThe shell command line
cwdstringWorking directory
envobjectExtra environment variables
statusstringqueued, starting, running, exited, failed, canceled, timed_out, or killed
cancelModestringgraceful or force, when canceled
canceledAtstringISO 8601 cancellation timestamp
exitCodeintegerProcess exit code, once exited
stdoutstringCaptured stdout
stderrstringCaptured stderr
stdoutTruncatedbooleanWhether stdout was truncated
stderrTruncatedbooleanWhether stderr was truncated
startedAtstringISO 8601 start timestamp
finishedAtstringISO 8601 finish timestamp
durationMsintegerWall-clock duration
collectStatusstringfailed, not_requested, partial, pending, skipped, or succeeded
collectErrorsarrayPer-path errors from artifact collection
killedReasonstringsandbox_destroyed, when the sandbox died under the command
terminationSignalstringSIGKILL or SIGTERM, when terminated

[!IMPORTANT] A non-zero exit code is not an API error. The HTTP request succeeds — check status and exitCode to learn how the process finished.

Run a command

POST /v1/sandboxes/{sandboxId}/commands/run — requires scope command:run, plus artifact:create if you pass collect. Accepts an Idempotency-Key header.

Runs the command and waits for it to exit before responding.

FieldTypeRequiredDefaultConstraints
commandstringYesNon-empty shell command line
cwdstringNoWorking directory inside the workspace
envobjectNoString map; keys with the CROWNEST prefix are reserved
timeoutMsintegerNo60000Max 600000
collectOnstringNo"success"success or always
collectarrayNoItems of shape path (required) and name (optional)

collect exports the listed workspace paths as artifacts after the command finishes; collectOn controls whether that happens only on a zero exit code (success) or regardless of outcome (always).

Terminal
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/commands/run \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9d2e4f1b-run-01" \
  -d '{
    "command": "python train.py",
    "timeoutMs": 120000,
    "collect": [{ "path": "/workspace/model.bin", "name": "model" }]
  }'

Returns 200 with the finished command:

{
  "command": {
    "id": "cmd_abc123",
    "sandboxId": "sbx_abc123",
    "command": "python train.py",
    "status": "exited",
    "exitCode": 0,
    "stdout": "epoch 1 complete\n",
    "stderr": "",
    "stdoutTruncated": false,
    "stderrTruncated": false,
    "durationMs": 8421,
    "collectStatus": "succeeded"
  }
}

Errors: invalid_request, reserved_env_key, forbidden, not_found, sandbox_destroyed, quota_exceeded, command_timed_out.

Start a command

POST /v1/sandboxes/{sandboxId}/commands/start — requires scope command:run. Accepts an Idempotency-Key header.

Starts the command in the background and returns immediately. The body is the same as run, without collect and collectOn.

Terminal
curl -X POST https://api.crownest.dev/v1/sandboxes/sbx_abc123/commands/start \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "command": "npm run dev", "timeoutMs": 600000 }'

Returns 202 with the command in queued or starting status. Follow it with get, logs, or the SSE stream.

Get a command

GET /v1/commands/{commandId} — requires scope command:read.

Returns the command record, including its current status, exit code, and captured output.

Terminal
curl https://api.crownest.dev/v1/commands/cmd_abc123 \
  -H "Authorization: Bearer $CROWNEST_API_KEY"

Returns 200 with { "command": { ... } }. Errors: forbidden, not_found.

Cancel a command

POST /v1/commands/{commandId}/cancel — requires scope command:cancel.

Stops a running command. The operation is idempotent by state — canceling an already-finished command returns the current record.

FieldTypeRequiredDefaultConstraints
modestringNo"graceful"graceful or force

Graceful cancellation sends SIGTERM and lets the process clean up; force sends SIGKILL.

Terminal
curl -X POST https://api.crownest.dev/v1/commands/cmd_abc123/cancel \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "mode": "force" }'

Returns 200 with { "command": { ... } } showing status canceled. Errors: forbidden, not_found.

Read logs

GET /v1/commands/{commandId}/logs — requires scope command:read.

Returns log chunks in sequence order. Use afterSeq to poll for new output incrementally.

ParameterTypeDefaultConstraints
streamstringcombinedstdout, stderr, or combined
afterSeqintegerReturn chunks after this sequence
limitinteger50Max 100
Terminal
curl "https://api.crownest.dev/v1/commands/cmd_abc123/logs?stream=stderr&afterSeq=12" \
  -H "Authorization: Bearer $CROWNEST_API_KEY"
{
  "data": [
    {
      "seq": 13,
      "type": "log",
      "timestamp": "2026-06-11T13:00:05.000Z",
      "data": "warning: deprecated flag\n",
      "stream": "stderr"
    }
  ],
  "hasMore": false,
  "nextSeq": 13
}

Each chunk has seq, type, timestamp, data, and stream. Pass the returned nextSeq as afterSeq on your next poll.

Stream logs over SSE

GET /v1/commands/{commandId}/stream — requires scope command:read.

Streams log output as server-sent events, ending when the command finishes. Event types:

EventMeaning
logA log chunk with seq, type, timestamp, data, and stream
heartbeatKeep-alive signal while the command is quiet
terminalThe command reached a terminal status
errorThe stream failed; reconnect or fall back to polling

To resume after a disconnect, send the last seq you saw, either as the afterSeq query parameter or as the standard Last-Event-ID header — SSE clients send Last-Event-ID automatically on reconnect. If the requested sequence has fallen outside the retention window, the stream returns a stream_gap error; fetch the full logs again instead.

Terminal
curl -N "https://api.crownest.dev/v1/commands/cmd_abc123/stream" \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Last-Event-ID: 13"

Get a logs download URL

POST /v1/commands/{commandId}/logs/download-url — requires scope command:read.

Returns a short-lived URL for downloading a full log stream, which is the right tool when output is too large to page through.

FieldTypeRequiredConstraints
streamstringYesstdout, stderr, or combined
Terminal
curl -X POST https://api.crownest.dev/v1/commands/cmd_abc123/logs/download-url \
  -H "Authorization: Bearer $CROWNEST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stream": "combined" }'
{
  "url": "https://api.crownest.dev/v1/...",
  "method": "GET",
  "expiresAt": "2026-06-11T13:15:00.000Z"
}

Next steps

  • Export command outputs durably in Artifacts.
  • Expose a long-running server in Previews.

On this page