> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Computers

> Sandboxed compute environments with a browser and filesystem.

A **computer** is a durable, sandboxed environment an [agent](/docs/api/agents) can use: a shell, a filesystem, and an optional browser constrained to an allowlist of domains. Computers can be paused to stop compute charges and snapshotted for fast restore.

Every [session](/docs/api/sessions) whose toolset needs a filesystem provisions one of these for itself and reports it as `computer_id`. Those appear in this list named `session {id}`, so you can tell them from the ones you created; their lifecycle is the session's, not yours.

## The computer object

<ResponseField name="id" type="string">Unique id (e.g. `cmp_01H...`).</ResponseField>
<ResponseField name="object" type="string">Always `computer`.</ResponseField>
<ResponseField name="name" type="string">Human-readable name.</ResponseField>
<ResponseField name="vcpu" type="integer">Provisioned vCPU for a micro-VM (1–16).</ResponseField>
<ResponseField name="memory_mb" type="integer">Provisioned memory in MiB (128–65536).</ResponseField>
<ResponseField name="disk_gb" type="integer">Root disk size in GiB.</ResponseField>
<ResponseField name="status" type="string">Lifecycle state — see [below](#lifecycle).</ResponseField>

<ResponseField name="browser" type="object">
  Browser configuration, or `null` if disabled. Every field has a default, so `{}` alone is a complete option and the browser reaches the open web. Drive it through the [Browser](/docs/api/browser) routes.

  <Expandable title="browser">
    <ResponseField name="allowed_domains" type="string[]">Domain allowlist (supports wildcards, e.g. `*.example.com`). Defaults to `["*"]`; an explicit `[]` is refused.</ResponseField>
    <ResponseField name="timeout_minutes" type="integer">Session lifetime, 1–360. Default `15`.</ResponseField>
    <ResponseField name="region" type="string">`us-west` (default), `us-east`, `eu-central` or `ap-southeast`.</ResponseField>
    <ResponseField name="keep_alive" type="boolean">Keep the session open across idle periods. Default `false`.</ResponseField>
    <ResponseField name="viewport" type="object">`{ width, height }` in pixels.</ResponseField>
    <ResponseField name="os" type="string">`windows`, `mac`, `linux`, `mobile` or `tablet` — the device the browser presents as.</ResponseField>
    <ResponseField name="block_ads" type="boolean">Default `false`.</ResponseField>
    <ResponseField name="solve_captchas" type="boolean">Default `true`.</ResponseField>
    <ResponseField name="stealth" type="boolean">Default `false`.</ResponseField>
    <ResponseField name="allow_writes" type="boolean">Permit actions that change a page (submit, buy, delete, …). Default `false`.</ResponseField>
    <ResponseField name="allow_extract" type="boolean">Permit reads from a browser that holds a saved login. Default `false`.</ResponseField>
    <ResponseField name="context_name" type="string">Open on a saved login of that name (see [Browser](/docs/api/browser#save-a-login)).</ResponseField>
    <ResponseField name="persist_context" type="boolean">Write the login state back to that context at close. Default `false`.</ResponseField>
    <ResponseField name="human_login" type="boolean">Hold the context for a person to sign in through the live view; locks it for 15 minutes. Default `false`.</ResponseField>
    <ResponseField name="proxy" type="boolean">Route through a residential proxy. Needs a bounded allow-list, not `*`. Default `false`.</ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="volume" type="object | null">Attached persistent volume. Always `null` — a volume separate from the boot disk is not provisioned today; size the root disk with `disk_gb`.</ResponseField>
<ResponseField name="snapshot_id" type="string | null">Snapshot this computer was restored from, if any.</ResponseField>
<ResponseField name="created_at" type="string">Creation timestamp.</ResponseField>

## Lifecycle

| Status      | Meaning                                                          |
| ----------- | ---------------------------------------------------------------- |
| `creating`  | Provisioning; not yet usable.                                    |
| `running`   | Ready and billable for compute.                                  |
| `paused`    | Stopped; storage cost only. Resume to use again.                 |
| `failed`    | Provisioning failed; the computer never became usable. Terminal. |
| `destroyed` | Permanently deleted. Terminal.                                   |

## Create a computer

`POST /v1/computers` → `202 Accepted`. Provisioning is asynchronous: the response returns immediately with `status: "creating"`. **Wait for readiness before using the computer**: poll `GET /v1/computers/{id}` until `status` is `running`. There is no computer lifecycle [event](/docs/api/events) to subscribe to — the event stream is session-scoped. If provisioning fails the computer moves to `failed`, and calls that need a live machine return `409` with code `computer_unavailable`.

<ParamField body="name" type="string">Human-readable name. Defaults to `computer`.</ParamField>
<ParamField body="vcpu" type="integer">Provisioned vCPU. Defaults to `2`.</ParamField>
<ParamField body="memory_mb" type="integer">Provisioned memory in MiB. Defaults to `4096`.</ParamField>
<ParamField body="disk_gb" type="integer">Root disk size in GiB. Defaults to `5`.</ParamField>
<ParamField body="browser" type="object | null">Browser config — `{}` enables one on the open web, `{ "allowed_domains": ["*.example.com"] }` scopes it; the full option object is [above](#the-computer-object). Defaults to `null`, which disables the browser.</ParamField>
<ParamField body="snapshot_id" type="string">Snapshot (`snp_…`) to restore this computer from.</ParamField>

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -fsSL https://api.vetta.sh/v1/computers \
    -H "authorization: Bearer sk_live_..." \
    -H "content-type: application/json" \
    -H "idempotency-key: $(uuidgen)" \
    -d '{ "name": "box", "vcpu": 4, "memory_mb": 8192, "disk_gb": 40, "browser": { "allowed_domains": ["*.example.com"] } }'
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={"system"}
  {
    "id": "cmp_01H8YZ...",
    "object": "computer",
    "name": "box",
    "vcpu": 4,
    "memory_mb": 8192,
    "disk_gb": 40,
    "status": "creating",
    "browser": {
      "allowed_domains": ["*.example.com"],
      "timeout_minutes": 15,
      "region": "us-west",
      "keep_alive": false,
      "block_ads": false,
      "solve_captchas": true,
      "stealth": false,
      "allow_writes": false,
      "allow_extract": false,
      "persist_context": false,
      "human_login": false,
      "proxy": false
    },
    "volume": null,
    "snapshot_id": null,
    "created_at": "2026-08-20T17:00:00Z"
  }
  ```
</ResponseExample>

## Retrieve & list

```bash theme={"system"}
GET /v1/computers/{id}     # retrieve one -> 200 (poll status here)
GET /v1/computers          # list (cursor-paginated) -> 200
```

See [Pagination](/docs/api/pagination) for list parameters.

## Rename

`PATCH /v1/computers/{id}` → `200 OK`. The name is the one field a computer has that you can change after creation; the body is strict, so any other field is `400` `validation_failed`. Returns the computer, and is recorded in the [audit log](/docs/api/audit-logs) as `computer.updated`.

<ParamField body="name" type="string" required>1–200 characters.</ParamField>

## Inspect

`GET /v1/computers/{id}/inspect` → `200 OK`. What the computer is doing right now, from two sources: the provider's own record of it (`status`, `last_active_at`, and `home` — the directory the `fs/*` calls are rooted in, or `null` when the provider does not confine them), and — only when that record says `running` — a single probe command run inside the machine. Inspecting never wakes a paused computer, so a paused or stopped one always answers with `probe: null`. A probe that cannot run or cannot be parsed is also `probe: null`, never an error; the numbers are a sample taken at `sampled_at`, not a metrics feed.

This call requires the **`computers:write`** scope. Despite the verb, it is not a plain read: the probe runs a command inside your machine, on compute you are billed for. Use `GET /v1/computers/{id}` for a read that does not touch the machine.

The `home` in the example below is illustrative. **Read it from this call rather than hardcoding a path** — it is what the machine's image reports, and it can differ between computers.

```json Response theme={"system"}
{
  "object": "computer_inspect",
  "id": "cmp_01H8YZ...",
  "status": "running",
  "last_active_at": "2026-09-08T03:19:11Z",
  "home": "/home/user",
  "probe": {
    "sampled_at": "2026-09-08T03:19:12Z",
    "uptime_seconds": 12345,
    "processes": 87,
    "load_1m": 0.42,
    "memory_used_mb": 1000,
    "memory_total_mb": 3952,
    "disk_used_gb": 1.0,
    "disk_total_gb": 5.0
  }
}
```

## Execute a command

`POST /v1/computers/{id}/exec` → `200 OK`. Runs a shell command and returns its output once it has finished. A `paused` computer is resumed first; a `creating`, `failed`, or destroyed one returns `409` with code `computer_unavailable`. Every run — by this route or the streamed one below — is recorded in the computer's [command history](#command-history).

<ParamField body="command" type="string" required>The command to run.</ParamField>
<ParamField body="timeout_ms" type="integer">Max runtime before the command is killed. Defaults to `30000`.</ParamField>

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/computers/cmp_01H8YZ.../exec \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "command": "ls -la $HOME" }'
```

```json Response theme={"system"}
{ "exit_code": 0, "stdout": "total 8\n...", "stderr": "", "duration_ms": 42 }
```

### Stream a command

`POST /v1/computers/{id}/exec/stream` → `200 OK`, `text/event-stream`. The same run as `exec`, delivered as it happens: the body is a stream of Server-Sent Events whose `data:` lines are JSON frames — zero or more `{"stdout": "…"}` / `{"stderr": "…"}` chunks in the order the process produced them, then exactly one `exit` frame and the stream closes. `command_id` names the row the run was recorded as. Same body as `exec`; the same `404`/`409`/`400` refusals are returned as ordinary JSON errors before the stream opens. The SDK reads this as `computers.execStream(id, command, { timeoutMs })`, an async iterable of frames.

```text Response theme={"system"}
data: {"stdout":"compiling\n"}

data: {"stderr":"warning: unused\n"}

data: {"exit":{"exit_code":0,"timed_out":false,"duration_ms":412,"command_id":"cmd_01J..."}}
```

## Command history

`GET /v1/computers/{id}/commands` → `200 OK`. Every command run on the computer through `exec` or `exec/stream`, newest first, cursor-paginated ([Pagination](/docs/api/pagination)). Each row keeps the first 65 536 characters of `stdout` and of `stderr`; `truncated` is `true` when either was cut. `actor` is the principal that ran it — an API key, a user, or the platform acting for a session. Commands the agent runs *inside* a session through its own tools are not listed here; they are in the session's [event stream](/docs/api/events).

This read requires the **`computers:write`** scope — the same scope that runs a command. It hands back what those commands printed, so it is not readable by a credential that could not have produced it.

<Note>
  **Retention.** Command output is stored for **30 days** and then deleted; the rest of a computer's history is deleted with the computer. It is stored exactly as the command produced it — nothing scans or redacts it — so a command that prints a credential stores that credential for the window. Print secrets to a file, or read them from the [vault](/docs/identity/vault), rather than to stdout.
</Note>

```json Response theme={"system"}
{
  "data": [
    {
      "id": "cmd_01J...",
      "object": "computer_command",
      "computer_id": "cmp_01H8YZ...",
      "command": "make",
      "exit_code": 0,
      "timed_out": false,
      "duration_ms": 412,
      "stdout": "compiling\n",
      "stderr": "",
      "truncated": false,
      "actor": { "type": "key", "id": "key_01J..." },
      "created_at": "2026-09-08T03:19:11Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}
```

## Pause & resume

Pausing stops compute charges while retaining state. Resuming makes it usable again.

```bash theme={"system"}
POST /v1/computers/{id}/pause    # -> 200, status: paused
POST /v1/computers/{id}/resume   # -> 200, status: running
```

```json Response theme={"system"}
{ "id": "cmp_01H8YZ...", "status": "paused" }
```

## Snapshot

`POST /v1/computers/{id}/snapshot` → `201 Created`. Captures the current filesystem state for fast restore. Pass the returned `snapshot_id` when creating a new computer.

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/computers/cmp_01H8YZ.../snapshot \
  -H "authorization: Bearer sk_live_..."
```

```json Response theme={"system"}
{ "snapshot_id": "snp_0368aa449de84e308959ca72744a3bd5", "computer_id": "cmp_01H8YZ...", "created_at": "2026-08-20T18:00:00Z" }
```

## Filesystem

The disk is reached through the governed `fs/*` operations — there is no separate upload/download channel. Each takes an absolute path. See [Filesystem](/docs/computer/filesystem) for limits and encoding details.

<Warning>
  **The `fs/*` operations are confined to the computer's home directory** — the `home` that [inspect](#inspect) reports. A path outside it (`/`, `/etc`, `/var`) is refused by the machine, including for `fs/list`, which reads the same governed file API as `read` and `write` rather than shelling out. Use [exec](#execute-a-command) to look at the rest of the disk.
</Warning>

### Read a file

`POST /v1/computers/{id}/fs/read` → `200`

<ParamField body="path" type="string" required>Absolute path of the file to read.</ParamField>
<ParamField body="encoding" type="string">`text` (default) or `base64`. Ask for `base64` when the file is not valid UTF-8 — decoding binary as text substitutes U+FFFD and loses bytes silently.</ParamField>

```json Response theme={"system"}
{ "path": "/home/user/notes.txt", "encoding": "text", "content": "hello\n" }
```

### Write a file

`POST /v1/computers/{id}/fs/write` → `200`

<ParamField body="path" type="string" required>Absolute path to write or overwrite.</ParamField>
<ParamField body="content" type="string" required>The file contents, in the named encoding.</ParamField>
<ParamField body="encoding" type="string">`text` (default) or `base64` for binary payloads.</ParamField>

### List a directory

`POST /v1/computers/{id}/fs/list` → `200`

<ParamField body="path" type="string" required>Absolute path of the directory; lists its immediate children.</ParamField>

Each entry carries the file's own metadata: `type` is `file`, `directory`, `symlink` or `other`; `mode` is the POSIX mode bits as an integer; `modified_at` is `null` when the filesystem does not report a time. The SDK returns the typed list as `computers.listDir(id, path)`.

### Create a directory

`POST /v1/computers/{id}/fs/mkdir` → `200`

<ParamField body="path" type="string" required>Absolute path to create; parents are created too (`mkdir -p`).</ParamField>

### Remove a path

`POST /v1/computers/{id}/fs/remove` → `200`

<ParamField body="path" type="string" required>Absolute path to remove; directories are removed recursively (`rm -rf`).</ParamField>

```bash theme={"system"}
curl -fsSL https://api.vetta.sh/v1/computers/cmp_01H8YZ.../fs/list \
  -H "authorization: Bearer sk_live_..." \
  -H "content-type: application/json" \
  -d '{ "path": "/home/user" }'
```

```json Response theme={"system"}
{
  "entries": [
    { "name": "report.md", "path": "/home/user/report.md", "type": "file", "size_bytes": 9, "mode": 33188, "modified_at": "2026-09-08T03:19:11Z" },
    { "name": "src", "path": "/home/user/src", "type": "directory", "size_bytes": 4096, "mode": 16877, "modified_at": "2026-09-08T03:18:40Z" }
  ]
}
```

<Note>
  To persist an artifact beyond the computer's lifetime, publish it to [Files](/docs/api/files) (Vetta object storage) rather than leaving it on the computer's disk.
</Note>

## Destroy

`DELETE /v1/computers/{id}` → `200 OK`. Permanently destroys the computer and its non-snapshotted disk.

```json Response theme={"system"}
{ "id": "cmp_01H8YZ...", "object": "computer", "deleted": true }
```

A computer a [session](/docs/api/sessions) provisioned for itself — listed as `session {id}` — cannot be
destroyed while that session can still run a turn: the call is `computer_unavailable` (409) naming
the session. Cancel the session to release the box, or let it finish; a session's computer is
destroyed with it, and one left resting at `idle` is reclaimed after an hour of silence.
