Skip to main content
Skip to main content

Managing and accessing the workspace

Every workspace serves one HTTP API. The web console, the zbox CLI and zbox mcp are all clients of that API. You authorize them in one of two ways: through your Zerops login, or with the workspace API key.

InterfaceUse it forSigned in with
Web consoleWorking with sandboxes in the browserYour Zerops login (from the Zerops GUI or the CLI), or the API key
HTTP APIYour own code, services and CIThe API key
zbox CLIYour terminal, scripts and agentsYour Zerops login, or the API key
MCP serverAI agents that run code in throwaway sandboxesSame as the CLI

API key​

The workspace API key is the service environment variable ZBOX_WORKSPACE_API_KEY, which Zerops generates when it creates the workspace (environment variables).

One key manages everything

The API key is the whole authority over the workspace. There are no scopes and no per-user keys. Anyone who holds it, and any sandbox you give it to, can create, read, change and terminate every sandbox, base, template, integration and webhook of the workspace. Keep it out of sandboxes unless you want them to manage the workspace. See isolation and security.

You don't need the key when you are logged in to Zerops. The Zerops GUI and the CLI reach the workspace through Zerops, which adds the key on the way. When you need it, for a script, CI or another machine, print it:

zbox workspace key

The CLI reads the key from the service with your Zerops login. Because the key is a sensitive variable, the CLI may ask you to confirm the access in the browser first.

The API key never opens a sandbox port. Port links use one-time tickets instead (access modes).

Rotate the API key​

  1. Edit ZBOX_WORKSPACE_API_KEY of the workspace service in the Zerops GUI.
  2. Reload the service. The reload applies the new key and keeps sandboxes running. Until then, only the old key works (environment variables).
  3. Update every script, CI secret and zbox workspace use that holds the old key.

After the reload, the old key is refused and browsers signed in to the console with it are signed out.

Logged-in access keeps the old key

The Zerops GUI and a logged-in CLI reach the workspace with an access that Zerops creates with the key of that moment. An access created before the reload keeps sending the old key until it expires, an hour after its last use, and the CLI fails with Missing or invalid Workspace API key (exit code 6). Reopen the console from the Zerops GUI, and fetch a new access for the CLI:

zbox workspace authorize <workspace-service-id>

Web console​

Each workspace serves a web console at the root of its API URL. It covers sandboxes (terminal, files, policy, snapshots, ports, sessions, events, metrics), images, integrations, activity, and the workspace itself.

Open it in one of these ways:

  • From the Zerops GUI: in the workspace service's left menu, open Web UI to see the console embedded in Zerops, or Open web UI in new tab. You are signed in through Zerops, with no key. Your role in the project decides whether you can open it.
  • From the CLI: zbox workspace console opens the console of the current workspace, signed in. For a workspace of your account it goes through Zerops. For one you added with zbox workspace use, it opens a one-time sign-in link that is valid for 60 seconds.
  • Directly: open the API URL, for example https://zbox-6da-8080.prg1.zerops.app, and paste the API key into the sign-in form.

Signing in with the key exchanges it once for a session cookie, so the key is never stored in the browser. The session lasts 7 days. Sign out ends it in that browser.

The console needs HTTPS

The session cookie works only over HTTPS. Over the Zerops VPN, the console at http://<hostname>.zerops:8080 loads, but signing in fails without an error. Use the Zerops GUI or the public API URL for the console. The API itself works over the VPN with a bearer token.

HTTP API​

The API URL is the Zerops subdomain of the workspace's port 8080, for example https://zbox-6da-8080.prg1.zerops.app (subdomain access). zbox workspace prints it on the API line. Other services in the project reach the API at http://<hostname>:8080 (project network). Every route is under /api/rest/.

Send the API key as a bearer token in the Authorization header, on every request including WebSocket upgrades. A key in the query string is refused. Only GET /api/rest/ping answers without the key.

export ZBOX_WORKSPACE_URL=https://zbox-6da-8080.prg1.zerops.app
export ZBOX_API_KEY=$(zbox workspace key)

# the workspace: capacity, usage, domains, policy
curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/workspace"
# live sandboxes, newest first
curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/sandbox-list"

The CLI, when logged in to Zerops, uses a different path: it calls the API through Zerops with a short-lived access token minted from your login, so no key is stored on your machine.

OpenAPI document​

Each workspace serves its API reference without the key:

  • Swagger UI: <API URL>/api/rest/swagger/
  • OpenAPI 3 document: <API URL>/api/rest/swagger/openapi.yml

The document describes the whole contract, including a few routes and fields that the beta does not act on yet. These docs describe only what works today. Generate a client from the document if you need one. There are no published SDKs.

Operations​

Requests that create, park, wake, fork, snapshot, restore or terminate sandboxes, pull or build bases, or build templates answer right away with an operation and do the work in the background (which verbs are operations). An operation has an id, a kind, a status (PENDING, RUNNING, FINISHED or FAILED), a result when it finished and an error when it failed.

To wait for one, long-poll it. waitSeconds holds the answer until the operation finishes or fails, for at most 30 seconds. If it is still running when the time is up, you get it as it is, so ask again:

curl -H "Authorization: Bearer $ZBOX_API_KEY" "$ZBOX_WORKSPACE_URL/api/rest/operation/<operation-id>?waitSeconds=30"

Creating a sandbox (POST /api/rest/sandbox) also takes waitSeconds in its body. The answer then carries the finished operation and the sandbox in its final state. GET /api/rest/operation-list lists recent operations, newest first.

The CLI waits for every operation by default. --no-wait prints the operation instead, --timeout stops waiting while the work goes on, and zbox operation wait <id> picks the wait up again.

Idempotency keys​

The requests that consume capacity require an Idempotency-Key header: create, fork, park, wake, snapshot, restore and terminate a sandbox, pull a base and create a template. Use a new unique value for each logical request, and reuse it only when you retry that same request.

  • The same key with the same request within 24 hours replays the first answer. Nothing runs twice.
  • The same key with a different request, or while the first request is still in progress, is refused with 409 idempotencyKeyConflict.
  • A request that failed releases its key, so you can retry with it.
  • Keys are shared by the whole workspace, not per client.
curl -X POST "$ZBOX_WORKSPACE_URL/api/rest/sandbox" \
-H "Authorization: Bearer $ZBOX_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"name": "app", "baseDigest": "<digest>", "networkMode": "PROJECT_INTERNET", "waitSeconds": 30}'

The CLI and the console send the header for you.

Errors​

Errors use the HTTP status code and one JSON shape. code is stable for your code to match on, and meta[].metadata lists problems by field:

{"error": {"code": "invalidUserInput", "message": "Invalid user input", "meta": [{"code": "invalidUserInput", "error": "Invalid user input", "metadata": {"networkMode": ["field is required"]}}]}}

For example, a wrong or missing key answers 401 unauthorized, an unknown sandbox 404 sandboxNotFound, and a full workspace 409 workspaceFull (capacity).

zbox CLI​

Install and log in as described in getting started. Every command explains itself with --help, and zbox --help lists the commands by task. The rules below hold for all of them.

Output and exit codes​

On a terminal the CLI prints tables. When stdout is a pipe or a file, it prints tab-separated values with one header line. --json prints the API objects as they are, and -q (--quiet) prints only ids, one per line. Progress and hints go to stderr. Under --json, an error is one JSON object on stderr.

id=$(zbox new -q --template dev)
zbox get "$id" --json
Exit codeMeaning
0OK
1Failed, for example a failed operation
2Wrong usage: bad flags or arguments, a request the API refused as invalid, a missing --yes
3Not found
4Conflict: the sandbox's state does not allow it, a name is taken, an operation is in progress
5Timed out waiting, the work may go on
6Not authorized: no login, no workspace, a rejected key
7Unavailable: the workspace is not reachable or is full, retry later
130Interrupted

zbox exec has exit codes of its own, see run a command.

Commands that delete something (remove, base remove, workspace teardown, and so on) ask for confirmation on a terminal. Without a terminal they refuse with exit code 2 unless you pass --yes (-y).

Several workspaces​

The CLI remembers the workspaces you have used and keeps one as current. Commands work on the current one.

# the Box Workspace services of your Zerops account, and the ones stored here
zbox workspace list
# store a workspace of your account; it is reached through Zerops, no key is stored
zbox workspace authorize <service-id>
# store a workspace outside your account by its API URL and key
zbox workspace use https://box-1a2-8080.prg1.zerops.app --api-key <key>
# make another stored workspace the current one
zbox workspace switch <name>
# work on another stored workspace for one command only
zbox list --workspace <name>
# drop the current workspace from the CLI, nothing is deleted
zbox workspace forget

zbox workspace use stores the key in the CLI's configuration file. ZBOX_WORKSPACE=<name> in the environment does the same as --workspace.

Scripts, CI and sandboxes​

These environment variables override what the CLI has stored, for scripts and CI:

VariableWhat it does
ZBOX_WORKSPACE_URL and ZBOX_API_KEYWork on the workspace at this API URL with this key, ignoring the stored ones. Set both or neither.
ZBOX_TOKENA Zerops personal access token used instead of the stored login, for commands such as zbox setup and zbox workspace teardown
ZBOX_CONFIGPath of the configuration file, ~/.config/zbox/config.json by default

The URL and key pair is the simplest way for CI, and for a sandbox that should manage its own workspace. A sandbox reaches its workspace's API only through the public API URL, so it needs a network mode with internet access.

ZBOX_WORKSPACE_URL=https://zbox-6da-8080.prg1.zerops.app ZBOX_API_KEY=<key> zbox list

MCP server​

zbox mcp serves the current workspace as an MCP server on stdio, so an AI agent can run code in sandboxes. Register it in Claude Code:

claude mcp add zbox -- zbox mcp
# spawn the sandboxes from the template "dev"
claude mcp add zbox -- zbox mcp --template dev

Any MCP client that starts a stdio server can run the same command. It has two tools:

  • run_code runs code in python (the default), javascript or bash, in /workspace, and answers its stdout, stderr and exit code. The image needs python3, node or bash for that. Without sandbox_id, every call creates a fresh sandbox named mcp-<time> and terminates it afterwards. With keep: true the sandbox stays and the answer carries its id, which later calls pass as sandbox_id to continue in it. timeout_seconds is at most 50.
  • kill_sandbox terminates a sandbox kept by run_code.

Sandboxes come from --template or --base. Without either, they come from the first ready template, or from the only ready base when there is no template. Their network mode is project_internet unless you pass --network. The server uses the CLI's current workspace, or the one --workspace or the environment variables above name.

Kept sandboxes outlive the session

A sandbox kept with keep: true keeps running after the agent session ends and holds its share of the workspace until kill_sandbox or zbox remove terminates it. Check with zbox list.