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

# Run sandboxes on your connected accounts

> Start, use, and end sandboxes on the sandbox providers your team connected to CostGraph, with AutoPilot choosing the provider and trying the next one when a start fails.

CostGraph can start sandboxes for you on the sandbox provider accounts your
team connected on Integrations. Your code calls the CostGraph sandbox API, and
no provider key is in your process. AutoPilot for sandboxes chooses the
provider for each sandbox and tries the next connected provider when a start
fails. The sandboxes run in your provider accounts, and each provider bills you
as before.

Use this when you build an agent or a tool for a team that connected its own
provider accounts. If your code holds the provider keys itself, use
[AutoPilot for sandboxes](https://docs.costgraph.ai/costgraph/autopilot/sandboxes.md) with ComputeSDK, or
the [routing API](https://docs.costgraph.ai/costgraph/autopilot/sandboxes-api.md), instead.

Sandboxes started this way appear on the **Sandboxes** tab of **AutoPilot**
with their provider, size, tags, and estimated cost.

## Requirements

* At least one sandbox provider connected on **Integrations > Sandbox**, as
  described in [Connect](https://docs.costgraph.ai/costgraph/workflows/managed-sandboxes.md#connect).
  [Supported sandbox providers](https://docs.costgraph.ai/costgraph/workflows/sandbox-providers.md) lists
  the providers.
* A CostGraph API key with the `sandbox:run` scope. Create it in
  **Settings > Personal API Keys**. Members signed in to CostGraph and the
  [CostGraph MCP server](https://docs.costgraph.ai/costgraph/mcp/install.md) can also call these endpoints.

The API key, base URL, tenant header, and `data` wrapper work as described in
[Requirements](https://docs.costgraph.ai/costgraph/autopilot/sandboxes-api.md#requirements) for the routing
API.

## How a provider is chosen

When you start a sandbox without naming a provider, CostGraph orders your
connected providers for the sandbox's size, as the
[routing API](https://docs.costgraph.ai/costgraph/autopilot/sandboxes-api.md#choose-a-provider) does. It then
tries them in that order until one starts the sandbox.

* **Your first connected provider is the baseline.** Providers are listed in
  the order you connected them, oldest first.
* **Your routing policy sets the order.** The policies are described in
  [Policies](https://docs.costgraph.ai/costgraph/autopilot/sandboxes.md#policies). Send `policy` to use
  another policy for one sandbox.
* **While AutoPilot for sandboxes is off, the order is yours.** CostGraph tries
  your providers in the order you connected them.
* **One connection serves each provider.** If you connected a provider more
  than once, CostGraph uses the oldest connection that works.
* **`provider` limits the start to one provider.** CostGraph then tries no other
  provider.

### When a start fails

CostGraph tries the next provider when a provider refuses to create the
sandbox, or when the sandbox is not ready within the readiness limit. Each
failed start is recorded with one of the following classes.

| Class | Meaning |
| - | - |
| `quota` | The provider account reached a limit, such as its sandbox quota. |
| `capacity` | The provider had no capacity for the sandbox. |
| `auth` | The provider refused CostGraph's key. |
| `timeout` | The sandbox was not ready within the readiness limit. |
| `other` | The provider refused the create for another reason. |

Failed starts lower the provider's success rate, which AutoPilot uses to rank
it for later sandboxes. Refer to
[Expected cost and start time](https://docs.costgraph.ai/costgraph/autopilot/sandboxes.md#expected-cost-and-start-time).

When a provider refuses CostGraph's key, the connection shows
`<Provider> refused CostGraph's key; reconnect it on Integrations.`
Later sandboxes skip that provider until you connect it again with a key that
works.

If no provider starts the sandbox, the request answers `502`.

### Readiness limit

A provider has 30 seconds to make a new sandbox ready. After that, CostGraph
counts the start as failed with class `timeout` and tries the next provider.
Admins can set the limit from 5 to 600 seconds with `ready_timeout_seconds` on
the [settings endpoint](https://docs.costgraph.ai/costgraph/autopilot/sandboxes-api.md#read-settings-and-results).
The limit also applies to workflow sandboxes that
[AutoPilot chooses](https://docs.costgraph.ai/costgraph/workflows/your-environment.md#choose-where-a-goal-runs).

## Start a sandbox

Send the size and your tags. Allow the request a few minutes, because CostGraph
can try more than one provider.

```shell theme={null}
curl https://api.costgraph.ai/api/v1/tenant/sandboxes \
  -H "X-API-Key: $COSTGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "size_class": "md",
    "metadata": { "agent": "researcher", "run": "run-42" },
    "lifecycle": { "timeout_ms": 3600000 }
  }'
```

The request takes the following fields. All are optional.

| Field | Description |
| - | - |
| `provider` | Start the sandbox only on this connected provider, such as `e2b`. |
| `size_class` | The sandbox size: `xs`, `sm`, `md`, `lg`, `xl`, or `2xl`. Refer to [Sizes](https://docs.costgraph.ai/costgraph/autopilot/sandboxes.md#sizes). |
| `vcpus`, `memory_mib` | The sandbox size as vCPUs and memory in MiB, for a size that is not a standard size. |
| `policy` | `cheapest`, `balanced`, or `fastest`, for this sandbox instead of your team's policy. |
| `metadata` | Up to 16 string tags. Keys can be up to 64 characters and values up to 256 characters. |
| `lifecycle.timeout_ms` | How long the provider keeps the sandbox before it ends it, from 1,000 to 86,400,000 milliseconds. |

Without a size, each provider starts its own default size, and CostGraph prices
the sandbox at that size.

CostGraph passes your tags to the provider as labels on the sandbox. The `run`
tag is the sandbox's run key. Use it to group the sandboxes of one agent run.
CostGraph also sets it on the sandbox as the provider label `costgraph.ai/run`.
Keys that start with `costgraph.ai/` are reserved for CostGraph, and CostGraph
drops them from your tags.

The response describes the sandbox.

```json theme={null}
{
  "data": {
    "provider": "daytona",
    "sandbox_id": "sbx_123",
    "decision_id": "6f1c2a9e-0b7d-4c41-9a53-2f7f4f0f9b11",
    "started_at": "2026-11-08T12:00:00Z",
    "price_per_hour": 0.0828,
    "routed_from": "e2b"
  }
}
```

| Field | Description |
| - | - |
| `provider` | The provider that started the sandbox. Use it in the sandbox's paths. |
| `sandbox_id` | The provider's sandbox ID. |
| `decision_id` | The ID of the routing answer that ordered the providers. |
| `started_at` | When the sandbox started. |
| `price_per_hour` | The list price per hour for the sandbox's size, in USD, or `null` when CostGraph has no price. |
| `routed_from` | The provider tried first, when the sandbox started on another provider. |

## Use a sandbox

Each sandbox has its own path, `/api/v1/tenant/sandboxes/{provider}/{sandbox_id}`.
These calls reach only sandboxes started through this API or the CostGraph MCP
tools. A sandbox stays on the provider connection that started it.

Every call on a sandbox records the time as its last use.

### List running sandboxes

`GET /api/v1/tenant/sandboxes` lists your running sandboxes, newest first. Each
sandbox has the following fields.

| Field | Description |
| - | - |
| `provider`, `sandbox_id` | The provider and the provider's sandbox ID. |
| `started_at` | When the sandbox started. |
| `last_used_at` | The last call on the sandbox, or `null` when there has been none. |
| `price_per_hour` | The list price per hour for the sandbox's size, in USD. |
| `metadata` | Your tags, and the tags that CostGraph adds with the `costgraph.ai/` prefix. |
| `seconds` | How long the sandbox has run, up to 24 hours. |
| `cost_usd` | The estimated cost so far. The run time is rounded up to the provider's billing increment and is at least its minimum billed time. |

Sandboxes started by workflows, CI jobs, or your own code are not listed.

### Read a sandbox

`GET /api/v1/tenant/sandboxes/{provider}/{sandbox_id}` checks with the provider
that the sandbox still runs. It returns the fields of the list with
`running: true`.

### Run a command

`POST /api/v1/tenant/sandboxes/{provider}/{sandbox_id}/exec` runs a shell
command in the sandbox.

```shell theme={null}
curl https://api.costgraph.ai/api/v1/tenant/sandboxes/daytona/sbx_123/exec \
  -H "X-API-Key: $COSTGRAPH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command": "python3 task.py", "cwd": "/workspace", "timeout_ms": 50000}'
```

| Field | Description |
| - | - |
| `command` | Required. The shell command, up to 64 KiB. |
| `cwd` | The directory to run the command in. |
| `env` | Up to 100 environment variables for the command. |
| `timeout_ms` | How long the command can run, up to 55,000 milliseconds. Without it, CostGraph waits up to 30 seconds. |
| `background` | Set to `true` to start the command and return at once. |

The response has `exit_code`, `stdout`, and `stderr`. Each output stream holds
at most 256 KiB, and `truncated` is `true` when CostGraph cut either one. For
a longer task, start it with `background: true`, write its output to a file, and
read the file when the task ends.

### Read and write files

`GET /api/v1/tenant/sandboxes/{provider}/{sandbox_id}/files?path=<path>` returns
the file at `path` as `content_base64`.

`PUT` on the same path with the body `{"content_base64": "<base64>"}` writes
the file and replaces any file at that path. The call answers `204`.

Files can be up to 4 MiB.

### End a sandbox

`DELETE /api/v1/tenant/sandboxes/{provider}/{sandbox_id}` ends the sandbox. The
call answers `204`.

## Use it from TypeScript

`costgraphConnections()` in `@costgraph/computesdk` is a ComputeSDK provider
that calls this API. Pass an API key with the `sandbox:run` scope as `apiKey`,
or an OAuth access token that can use the CostGraph MCP tools as
`accessToken`.

```ts theme={null}
import { compute } from 'computesdk';
import { costgraphConnections } from '@costgraph/computesdk';

compute.setConfig({
  provider: costgraphConnections({ apiKey: process.env.COSTGRAPH_API_KEY, policy: 'cheapest' }),
});

const sandbox = await compute.instances.create({ sizeClass: 'md', metadata: { agent: 'researcher' } });
const { stdout, truncated } = await sandbox.runCommand('python3 task.py', { timeout: 50_000 });
await sandbox.filesystem.writeFile('/tmp/input.json', JSON.stringify(input));
const report = await sandbox.filesystem.readFile('/tmp/report.md');
await sandbox.destroy();
```

Sandboxes support `runCommand`, `getInfo`, `destroy`, and the file methods
`filesystem.readFile` and `filesystem.writeFile`. Snapshots, volumes, and other
instance methods are not available. `instances.list()` and `getById`
reach the running sandboxes started through this API or the CostGraph MCP
tools. `timeout` on `create()` becomes `lifecycle.timeout_ms`.

`costgraphConnections()` accepts the following options.

| Option | Default | Description |
| - | - | - |
| `apiKey` | None | A CostGraph API key with the `sandbox:run` scope. Set this or `accessToken`. |
| `accessToken` | None | An OAuth access token that can use the CostGraph MCP tools, sent as a bearer token. |
| `baseUrl` | `https://api.costgraph.ai` | The CostGraph API address. |
| `tenantId` | None | The tenant, sent as the `X-CostGraph-Tenant-ID` header. |
| `policy` | Your team's policy | `cheapest`, `balanced`, or `fastest` for these sandboxes. |
| `provider` | None | Start every sandbox on this connected provider, such as `e2b`. |
| `fetch` | Global `fetch` | A custom `fetch` implementation. |

Without a `run` tag, the package generates a run key for each sandbox. Read it
with `runKey(sandboxId)` on the provider.

## How sandboxes stop

CostGraph does not end a sandbox on its own. A sandbox stops when you end it, or
when the provider ends it at the end of `lifecycle.timeout_ms` or its own
timeout. When a call finds that the provider has ended the sandbox, CostGraph
records it as stopped and the call answers `404`.

Each stopped sandbox has one of the following stop reasons on the
[runs endpoint](https://docs.costgraph.ai/costgraph/autopilot/sandboxes-api.md#read-settings-and-results).

| Reason | Meaning |
| - | - |
| `destroyed` | You ended the sandbox. |
| `timeout` | The provider ended the sandbox at the end of its lifetime. |
| `lost` | The sandbox ended in another way, for example in the provider's console. |

A running sandbox keeps billing until it stops. When a sandbox's cost since its
last use reaches the [insight cost floor](https://docs.costgraph.ai/costgraph/insights.md#cost-floor),
CostGraph opens a [forgotten sandbox insight](https://docs.costgraph.ai/costgraph/insights.md#sandboxes).

## Errors

Errors answer with a message that says what to do.

| Status | Meaning |
| - | - |
| `400` | The request is not valid. The message names the field. |
| `403` | The API key lacks the `sandbox:run` scope. |
| `404` | The sandbox does not exist, was not started through this API or the MCP tools, or has ended. |
| `409` | No sandbox provider is connected, or the named provider is not connected. Connect it on **Integrations**. |
| `413` | The file is larger than 4 MiB. |
| `422` | The path is not a file that can be read. |
| `502` | No connected provider could start the sandbox. Retry, or check the providers on **Integrations**. |
| `503` | The provider is unavailable. Retry shortly. |
| `500` | The request could not be completed. Retry shortly. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.