Cubicle

HTTP API

The Cubicle HTTP API at https://api.cubicle.sh/v1, with the base URL, a first authenticated curl request, JSON, UUID and ISO 8601 conventions, Box and organization references in paths, the OpenAPI 3.1 document for generating clients in other languages, and an index of every endpoint with its TypeScript SDK method.

The HTTP API is JSON over HTTPS. The SDK, the CLI and the Dashboard all use it. Every route is under /v1:

https://api.cubicle.sh/v1

The TypeScript SDK wraps this API: it waits for asynchronous requests, retries safely and paces its requests for you. There is no official SDK for other languages yet. From other languages, shell scripts and tools, call the HTTP API directly, or generate a client from the OpenAPI document.

Make a first request

You need an API key (cbk_…) that belongs to a service account. Sign in and API keys shows how to create one.

export CB_SDK_API_KEY=cbk_…

curl https://api.cubicle.sh/v1/me \
  -H "Authorization: Bearer $CB_SDK_API_KEY"

The response says who the key belongs to:

{
  "id": "8e0a2c4e-6b8d-4f1a-a3c5-9e1b3d5f7a9c",
  "identity": "acme/ci",
  "operator": false,
  "orgs": [
    {
      "id": "6b1f3d2a-8c4e-4f7a-9b2d-1e5c7a9f3b6d",
      "name": "acme",
      "role": "member",
      "personal": false,
      "boxCreateAllowed": true
    }
  ],
  "apiKey": {
    "id": "4b6d8f0a-2c4e-4a6b-8d0f-1a3c5e7b9d2f",
    "orgId": "6b1f3d2a-8c4e-4f7a-9b2d-1e5c7a9f3b6d",
    "scope": "write"
  }
}
  • identity is the key's service account, ORG/NAME.
  • orgs holds only the key's organization. boxCreateAllowed says whether you can create Boxes in it.
  • apiKey.scope is write or read. A read key may only send GET requests.

Next, create a Box. The request returns 202 Accepted at once, with the Box in state pending:

curl -X POST https://api.cubicle.sh/v1/orgs/acme/boxes \
  -H "Authorization: Bearer $CB_SDK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"name": "job-1"}'

Poll the Box until ssh.ready is true, as Following a request describes, then run commands in it with Run a command. Delete it with Delete a Box when you are done.

Conventions

TopicRule
AuthenticationAuthorization: Bearer <token> on every request except the two device sign-in routes. See Authentication.
PathsMatched exactly, with no trailing slash. An unknown path returns 404 not_found. A known path with another method returns 405 method_not_allowed.
Request bodiesJSON, at most 64 KiB. Send Content-Type: application/json. An empty body counts as {}, so a route whose fields all have defaults needs no body. Unknown fields are ignored.
ResponsesJSON, with content-type: application/json. A 204 response has no body.
Success statuses200 for reads and immediate changes, 201 when a service account, an API key or an SSH key is created, 202 when a request is accepted and carried out afterwards, and 204 when there is nothing to return.
IDsUUIDs, such as 0f6c2a5e-3b1d-4c8e-9a7f-2d4e6b8c1a3f. Boxes, organizations, users, service accounts, operations, snapshots, keys and sessions all have one.
TimestampsISO 8601 strings, such as 2026-11-05T10:14:21.000Z. Parse them with an ISO 8601 parser.
SizesIntegers: vcpus, memoryMiB and diskGiB.
DurationsMilliseconds in requests (timeoutMs). Seconds in responses (ageSeconds, retryAfterSeconds, expiresIn, interval).
ListsBare JSON arrays, never wrapped in an object, and not paginated: a list returns every item. List a Box's operations is the exception: it returns the 50 most recent.
Response headersEvery response has cache-control: no-store, x-content-type-options: nosniff and x-frame-options: DENY. There are no CORS headers, so code in a web page cannot call the API, and no rate-limit headers.
Asynchronous workLifecycle changes, deletion, exec, snapshot and restore return 202. Poll until the work is done. See Requests and waiting.
Errors{ "error": { "code": "…", "message": "…" } } with a matching status. See Errors.
Rate limitsEach user or service account has a request budget. See Rate limits.

Box and organization references

{box} in /v1/boxes/{box}/… takes a Box ID, ORG/NAME or a bare NAME. Box references explains how each form resolves. In a path:

  • Encode the slash in ORG/NAME as %2F, as in /v1/boxes/acme%2Fjob-1. Unencoded, the path names a different route.
  • Narrow a bare NAME with ?org=, which takes an organization's name or ID: /v1/boxes/job-1?org=acme. ?org= is ignored when {box} is an ID.

{org} in /v1/orgs/{org}/… is the organization's name or ID. An organization where you are not an active member returns 404 not_found, and so does every organization except an API key's own.

Version and OpenAPI document

/v1 is the only version of the API.

https://docs.cubicle.sh/openapi.json describes every public endpoint as an OpenAPI 3.1 document: parameters, request and response schemas, examples, errors and code samples (x-codeSamples). The endpoint pages below are generated from it. Give it to a client generator, or to a coding agent that needs the whole API. See Docs for AI tools for the plain-text docs.

Endpoints

Routes that accept API keys have an SDK method. The others, apart from the two device sign-in routes that need no credentials, need a sign-in session; an API key gets 403 api_key_not_allowed there. See Authentication.

Boxes

Create Boxes, read them, and change their desired state.

MethodPathWhat it doesSDK method
GET/v1/orgs/{org}/boxesLists one organization's Boxes, including Boxes still being deleted.cubicle.boxes.list({ org })
POST/v1/orgs/{org}/boxesCreates a Box from a base image or a snapshot.cubicle.boxes.create()
GET/v1/boxesLists the Boxes of all your organizations, with grants and your access.cubicle.boxes.list() without an org
GET/v1/boxes/{box}Returns one Box with its desired and observed state.cubicle.boxes.get(); cubicle.boxes.waitUntil() and waitUntilReady() poll it
POST/v1/boxes/{box}/startStarts a Box, or resumes a hibernated one.cubicle.boxes.start()
POST/v1/boxes/{box}/stopStops a Box and keeps its disk.cubicle.boxes.stop()
POST/v1/boxes/{box}/hibernateSaves a running Box's memory to its disk and stops it.cubicle.boxes.hibernate()
DELETE/v1/boxes/{box}Deletes a Box, its disk and its snapshots.cubicle.boxes.delete()
GET/v1/orgs/{org}/basesLists the base images and their default sizes.cubicle.listBases()

Commands

Run commands in a Box and follow the operations that carry them out.

MethodPathWhat it doesSDK method
POST/v1/boxes/{box}/execRuns a command in a Box and returns its operation.cubicle.boxes.exec()
GET/v1/boxes/{box}/operationsLists a Box's 50 most recent operations.cubicle.boxes.listOperations()
GET/v1/operations/{operation}Returns one exec, snapshot or restore operation.cubicle.operations.get(), cubicle.operations.wait()

Snapshots

Save a stopped Box's disk, restore it, and create Boxes from snapshots.

MethodPathWhat it doesSDK method
POST/v1/boxes/{box}/snapshotsTakes a snapshot of a stopped Box's disk.cubicle.boxes.snapshot()
GET/v1/boxes/{box}/snapshotsLists a Box's snapshots, including pending and failed ones.cubicle.boxes.listSnapshots()
POST/v1/boxes/{box}/restoreReplaces a stopped Box's disk with one of its snapshots.cubicle.boxes.restore()
GET/v1/orgs/{org}/snapshotsLists the complete snapshots you may create Boxes from.cubicle.listSnapshots()

Box access

Grant people and service accounts access to a Box, and read its SSH details.

MethodPathWhat it doesSDK method
GET/v1/boxes/{box}/grantsLists a Box's grants, revoked ones included.None
PUT/v1/boxes/{box}/grants/{user}Gives a member of the organization a grant on a Box.None
DELETE/v1/boxes/{box}/grants/{user}Revokes a member's grant on a Box.None
GET/v1/boxes/{box}/sshReturns your SSH user, the Box's host key and whether you can connect.None

Organizations

List and change an organization's members.

MethodPathWhat it doesSDK method
GET/v1/orgs/{org}/membersLists an organization's members, people and service accounts.None
POST/v1/orgs/{org}/membersGives an existing user a role in the organization.None
DELETE/v1/orgs/{org}/members/{user}Ends a membership, or deletes a service account.None

Service accounts

Manage non-interactive members and their API keys.

MethodPathWhat it doesSDK method
GET/v1/orgs/{org}/service-accountsLists service accounts with their API keys.None
POST/v1/orgs/{org}/service-accountsCreates a service account.None
DELETE/v1/orgs/{org}/service-accounts/{account}Deletes a service account and revokes its keys.None
POST/v1/orgs/{org}/service-accounts/{account}/keysCreates an API key and returns its token once.None
DELETE/v1/orgs/{org}/service-accounts/{account}/keys/{key}Revokes an API key.None

Account

The signed-in caller, their sessions and their SSH keys.

MethodPathWhat it doesSDK method
GET/v1/meReturns the caller and the organizations it can use.cubicle.me()
GET/v1/sessionsLists your live sign-in sessions.None
DELETE/v1/sessions/{session}Ends one of your sign-in sessions.None
GET/v1/keysLists your SSH public keys.None
POST/v1/keysAdds an ssh-ed25519 public key to your account.None
DELETE/v1/keys/{key}Removes one of your SSH keys.None

Sign-in

Sign a device in with the device authorization flow, and sign out.

MethodPathWhat it doesSDK method
POST/v1/auth/deviceStarts a device sign-in. Needs no credentials.None
POST/v1/auth/device/tokenExchanges an approved device code for a sign-in session.None
POST/v1/auth/logoutEnds the sign-in session that sends the request.None

On this page