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

# Run commands

> Run any shell command inside an instance from your backend, the escape hatch for anything the API does not wrap.

`POST /v1/instances/{id}/exec` runs a shell command inside the instance, straight from your backend. It is the escape hatch for anything the API does not wrap as its own call.

The command runs through `sh -c` as the image's default user, on the same box the agent works on, so it sees the agent's files, tools, and credentials.

## Request

<ParamField body="command" type="string" required>
  The shell command to run inside the instance. It is passed to `sh -c`, so pipes, redirects, and `&&` chains all work.
</ParamField>

<Note>
  Only `running` instances accept commands. Exec against any other status returns `400 invalid_request` (a deleted instance returns `404 not_found`), and it does not wake a `sleeping` instance: [start](/agents-api/instances#start) it, or hit one of its URLs, first. If the platform cannot reach the instance at all, you get `502 provisioning_failed`.
</Note>

## Response

A command that runs but exits nonzero is a normal result: you get `200` with its `exit_code`, `stdout`, and `stderr`. Errors are reserved for the platform, not your command.

<ResponseField name="exit_code" type="integer">
  The command's exit code. Nonzero is still a `200`; read this to branch.
</ResponseField>

<ResponseField name="stdout" type="string">
  Standard output, capped at 512 KB. See `truncated`.
</ResponseField>

<ResponseField name="stderr" type="string">
  Standard error, with its own separate 512 KB cap.
</ResponseField>

<ResponseField name="truncated" type="boolean">
  `true` when either stream spilled past its 512 KB cap. The middle of the output is cut and a truncation marker is left in its place.
</ResponseField>

<Note>
  `exit_code` values 125, 126, and 127 may come from the container runtime rather than your command, for example 127 when the binary is not found. A command runs for up to 280 seconds, after which the call fails with `502 provisioning_failed`; start longer jobs in the background (`nohup ... &`) and poll with a second exec.
</Note>

## Example

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.agent37.com/v1/instances/ab12cd34ef/exec \
    -H "Authorization: Bearer sk_live_..." \
    -H "Content-Type: application/json" \
    -d '{ "command": "node --version" }'
  ```

  ```python python theme={null}
  import requests

  resp = requests.post(
      "https://api.agent37.com/v1/instances/ab12cd34ef/exec",
      headers={"Authorization": "Bearer sk_live_..."},
      json={"command": "node --version"},
  )
  result = resp.json()
  print(result["exit_code"], result["stdout"])
  ```

  ```javascript node theme={null}
  const resp = await fetch(
    "https://api.agent37.com/v1/instances/ab12cd34ef/exec",
    {
      method: "POST",
      headers: {
        Authorization: "Bearer sk_live_...",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ command: "node --version" }),
    }
  );
  const result = await resp.json();
  console.log(result.exit_code, result.stdout);
  ```

  ```json response theme={null}
  {
    "exit_code": 0,
    "stdout": "v24.2.0\n",
    "stderr": "",
    "truncated": false
  }
  ```
</CodeGroup>

## Build on exec

Anything the API does not wrap as its own endpoint, you build on `exec`. For moving files, prefer the instance's own [files endpoints](/agents-api/files) at `https://{instanceId}.agent37.app` — `PUT /v1/files/content` to upload, `GET /v1/files/content` to download — but a quick text read works over exec too. A "Download the report" button in your product can be one exec call that reads the file the agent wrote:

```bash curl theme={null}
curl -X POST https://api.agent37.com/v1/instances/ab12cd34ef/exec \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "command": "cat ~/reports/ev-makers-memo.md" }'
```

Pushing a file in is the same trick in reverse. Encode it on your side and decode it inside the instance:

```bash curl theme={null}
curl -X POST https://api.agent37.com/v1/instances/ab12cd34ef/exec \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "command": "mkdir -p ~/data && echo aGVsbG8sd29ybGQK | base64 -d > ~/data/ev-prices.csv" }'
```

For binary or large files, use the [files endpoints](/agents-api/files) on the instance URL instead — `GET /v1/files/content` streams a download of any size, with no 512 KB cap — or stage them at a URL your backend controls and `curl` them down from inside the instance.
