# Commands and files

https://instabox.sh/docs/api/commands.md

Run a command in a machine, and read, upload and download its files.

They need a running machine: one paused for being idle is woken first, any other gets `409 machine_not_running`. At most 32 requests per user run at once, across all your sandboxes. Commands, file reads and writes, desktop, port-forward and Secret refresh calls share this limit. A background command counts only while it starts. More get a retryable `429 quota_exceeded`.

- `POST /v1/machines/{id}/exec`: Run a command
- `GET /v1/machines/{id}/files`: Read a file or directory
- `PUT /v1/machines/{id}/files`: Upload a file
- `GET /v1/machines/{id}/files/content`: Download a file
- `POST /v1/machines/{id}/files/upload-url`: Get an upload URL

## Run a command

`POST /v1/machines/{id}/exec`

Runs a command as the machine’s user and returns its output when it exits: `cmd` is a shell string run with `bash -lc`; `argv` is a program and its arguments, run with no shell, so each argument arrives exactly as sent. Set exactly one of them. A non-zero exit code is still a `200` result. With `Accept: text/event-stream` the output is streamed as server-sent events while the command runs: `stdout` and `stderr` events (`{"data"}`), then one `exit` event with the result (empty output) or an `error` event. Run jobs longer than 10 minutes in the background. The call returns when the command exits, even if a process it started in the background keeps its stdout or stderr open; that process keeps running, output written later than about a second after the exit is not returned, and its later writes to that output fail with a broken pipe, so redirect a server’s stdout and stderr to a file (`nohup server >log 2>&1 &`). If the connection to the command is lost before it exits (the client disconnects, an API server restarts), the command and its process group are killed rather than left running; a restarting API server answers `503 host_unavailable` (`retryable`).

**Path parameters**

- `id` (string, required): The machine's ID (`m_agqteugyy5px6hzeadbh4ldjo4`).

**Body**

- `cmd` (string): The shell command, run with `bash -lc`. Set this or `argv`.
- `argv` (string[]): The program and its arguments, run with no shell: `$`, `*`, quotes and spaces reach the program as written. `["printf", "%s|", "a b", "$X"]` prints `a b|$X|`. Set this or `cmd`.
- `cwd` (string): Working directory. Default: home, `/data/home`.
- `envs` (object): Environment variables to set.
- `stdin` (string): Fed to the command, then end of file. At most 1 MiB.
- `timeoutSeconds` (integer): Kill the command after this long (exit code 124). Default 60, at most 600.

**Request**

```bash
curl -X POST https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/exec -H "X-API-Key: $INSTABOX_API_KEY" -H 'Content-Type: application/json' \
  -d '{"cmd": "echo hello"}'
```

**Response** (200 OK)

```json
{
  "stdout": "hello\n",
  "stderr": "",
  "exitCode": 0,
  "timedOut": false,
  "truncated": false
}
```

## Read a file or directory

`GET /v1/machines/{id}/files`

Lists a directory (up to 5000 entries) or returns a text file (up to 256 KiB), read live as root.

**Path parameters**

- `id` (string, required): The machine's ID (`m_agqteugyy5px6hzeadbh4ldjo4`).

**Query parameters**

- `path` (string): An absolute path. Default: `/`.

**Request**

```bash
curl "https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/files?path=/data/home" -H "X-API-Key: $INSTABOX_API_KEY"
```

**Response** (200 OK)

```json
{
  "path": "/data/home",
  "type": "dir",
  "size": 0,
  "mode": "drwxr-xr-x",
  "modTime": "2026-10-13T09:30:12Z",
  "entries": [
    { "name": "app", "type": "dir", "size": 0, "mode": "drwxr-xr-x", "modTime": "2026-10-13T09:28:40Z" },
    { "name": "notes.txt", "type": "file", "size": 5, "mode": "-rw-r--r--", "modTime": "2026-10-13T09:30:12Z" }
  ]
}
```

## Upload a file

`PUT /v1/machines/{id}/files`

Streams the raw request body into the file, as the machine’s user: any bytes, up to 5 GiB. The file appears only once every byte arrived; the answer has its SHA-256. A missing parent directory is `404 missing_parent` unless `parents=true`; one you cannot write is `403 permission_denied`; a directory is `409 not_a_file`. A transfer idle for 60 seconds is dropped. `201` for a new file, `200` for a replaced one.

**Path parameters**

- `id` (string, required): The machine's ID (`m_agqteugyy5px6hzeadbh4ldjo4`).

**Query parameters**

- `path` (string, required): An absolute path.
- `mode` (string): Octal permissions, e.g. `0755`. Default: an existing file’s, else `0644`.
- `parents` (boolean): Create missing parent directories.

**Request**

```bash
curl -X PUT "https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/files?path=/data/home/x.tgz" -H "X-API-Key: $INSTABOX_API_KEY" \
  --data-binary @x.tgz
```

**Response** (201 Created)

```json
{
  "path": "/data/home/x.tgz",
  "size": 52428800,
  "sha256": "3f0a…",
  "mode": "0644",
  "created": true
}
```

## Download a file

`GET /v1/machines/{id}/files/content`

Streams a file’s raw bytes, any size, as the machine’s user, with its exact `Content-Length`. Missing is `404 not_found`, a directory `409 not_a_file`, unreadable `403 permission_denied`.

**Path parameters**

- `id` (string, required): The machine's ID (`m_agqteugyy5px6hzeadbh4ldjo4`).

**Query parameters**

- `path` (string, required): An absolute path.

**Request**

```bash
curl "https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/files/content?path=/data/home/x.tgz" -H "X-API-Key: $INSTABOX_API_KEY" -o x.tgz
```

**Response** (200 OK)

```json
(the file’s bytes)
```

## Get an upload URL

`POST /v1/machines/{id}/files/upload-url`

A signed URL that takes `PUT` of one file’s bytes with no other credential, until it expires (default 15 minutes, at most 1 hour). It binds the path, `mode` and `parents`, and stops working if your API key is revoked. Same limits as uploading a file.

**Path parameters**

- `id` (string, required): The machine's ID (`m_agqteugyy5px6hzeadbh4ldjo4`).

**Body**

- `path` (string, required): An absolute path.
- `mode` (string): Octal permissions.
- `parents` (boolean): Create missing parent directories.
- `expiresInSeconds` (integer): Default 900, at most 3600.

**Request**

```bash
curl -X POST https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/files/upload-url -H "X-API-Key: $INSTABOX_API_KEY" -H 'Content-Type: application/json' \
  -d '{"path": "/data/home/big.bin"}'
curl -X PUT --upload-file big.bin "$URL"
```

**Response** (201 Created)

```json
{
  "url": "https://api.instabox.sh/v1/machines/m_agqteugyy5px6hzeadbh4ldjo4/files?path=%2Fdata%2Fhome%2Fbig.bin&token=…",
  "method": "PUT",
  "path": "/data/home/big.bin",
  "expiresAt": "2026-10-13T09:45:12Z",
  "maxBytes": 5368709120
}
```
