Cubicle

Run a command

POST
/v1/boxes/{box}/exec

Queues argv to run in the Box and returns an operation with 202 Accepted. Poll the operation until its state is done, failed or unknown; a done exec's result holds the exit code and output. See Following a request.

The caller needs an explicit grant on the Box: the owner or admin role is not enough. API keys need the write scope, and their service account needs the grant.

  • The Box must be desired running. Wait until ssh.ready is true first: an exec accepted before the Box is running can fail with box is not running at the requested generation.
  • The command runs as the caller's guest account, in its home directory unless cwd is set, without a shell, a TTY or stdin; use ["sh", "-c", "…"] for shell syntax. It has passwordless sudo only once the caller has an SSH key, so a service account's commands cannot rely on sudo; see Root and sudo.
  • timeoutMs defaults to 60,000 (60 s), and larger values than 600,000 (10 min) are lowered to it without an error. A command that runs out of time is killed and reports timedOut: true and exit code 124.
  • maxOutputBytes defaults to 65,536 (64 KiB), and larger values than 1,048,576 (1 MiB) are lowered to it. It is one budget for stdout and stderr together; the rest is discarded and truncated is true.
  • A non-zero exit code or a timeout still ends done. failed comes with an error, for example when the Box stopped or the caller lost its grant before the command started. unknown means it was interrupted and may or may not have run. Neither is run again; see Operations record every request.
  • Only the caller sees the result. Other members of the organization can read the operation, but its result is null for them.

With an Idempotency-Key, repeating the request with the same body returns 202 with the operation the first request created, and runs nothing. Output is returned only when the command ends. See Run commands.

Authorization

headerAuthorizationBearer <token>

A service account API key (cbk_…), used directly as the bearer token. read keys may call only GET routes.

Path Parameters

box*string

The Box's ID, ORG/NAME with the slash encoded as %2F, or a bare NAME, which ?org= narrows. See Box references.

Query Parameters

org?string

The organization's ID or name, to narrow a bare NAME in {box}. Ignored for an ID or ORG/NAME.

Header Parameters

Idempotency-Key?string

Makes the request safe to retry: repeating it with the same key and body returns the first result instead of doing the work again. 1 to 200 characters; a UUID is a good choice.

Length1 <= length <= 200

Request Body

application/json
  1. body
argv*array<>
Items1 <= items <= 1024
timeoutMs?integer
Range100 <= value <= 9007199254740991
maxOutputBytes?integer
Range0 <= value <= 9007199254740991
cwd?string
Match^\/.*
Formatstarts_with
Lengthlength <= 4096

Response Body

The exec operation: new, or the original one for an Idempotency-Key replay.

application/json
  1. response
id*string

The operation's ID.

boxId*string

The ID of the Box it runs on.

type*string

exec for a command, snapshot or restore.

Value in"exec""snapshot""restore"
state*OperationState

pending until the Box's Node picks it up, then running, and finally done, failed or unknown. unknown means it was interrupted and its outcome is not known; it is never retried.

Value in"pending""running""done""failed""unknown"
attempt*number

How many times a Node has picked it up: 0 while pending, then 1.

progress*|

Reserved for progress notes. Current Nodes do not send any, so it is null.

result*unknown

null until done. A done exec holds an ExecResult, shown only to the member who requested it and null for everyone else. A done restore holds { "snapshotId": … }. A done snapshot holds an internal manifest; read the snapshot from the Box's snapshot list instead.

error*|

Why it failed or became unknown; otherwise null.

createdAt*string

When it was requested.

startedAt*|

When a Node picked it up, or null while pending.

completedAt*|

When it reached done, failed or unknown, or null before.

Example Request
curl -X POST 'https://api.cubicle.sh/v1/boxes/acme%2Fjob-1/exec' \  -H 'Idempotency-Key: 4f1c7c1e-6a43-4d5e-9b7e-0d2f6c0e9a51' \  -H 'Content-Type: application/json' \  -d '{  "argv": [    "uname",    "-a"  ],  "timeoutMs": 30000}'
Example Response
{  "id": "9d2e4f6a-1b3c-4d5e-8f7a-6b8c0d2e4f1a",  "boxId": "0f6c2a5e-3b1d-4c8e-9a7f-2d4e6b8c1a3f",  "type": "exec",  "state": "pending",  "attempt": 0,  "progress": null,  "result": null,  "error": null,  "createdAt": "2026-11-05T10:16:00.000Z",  "startedAt": null,  "completedAt": null}