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

# Manage sandboxes

> Create sandboxes with the TypeScript, Python, or Go SDK, the Baseten CLI, or the dashboard, and inspect, update, and delete them.

Create a sandbox for an agent's task, wait for it to deploy, and delete it when
the work finishes. Use a Baseten SDK to manage sandboxes from your application.
Each SDK creates and refreshes [sandbox access tokens](/sandboxes/authentication)
for you. Use the [Baseten CLI](/reference/cli/baseten/overview) or the dashboard
to inspect and troubleshoot sandboxes by hand.

You need permission to create resources in your team.

## Set up

Install the CLI or an SDK:

<Tabs>
  <Tab title="TypeScript">
    ```bash theme={"system"}
    npm install @basetenlabs/sandbox undici
    ```
  </Tab>

  <Tab title="Python">
    ```bash theme={"system"}
    pip install baseten "httpx[http2]"
    ```
  </Tab>

  <Tab title="Go">
    ```bash theme={"system"}
    go get github.com/basetenlabs/baseten-go/sandbox
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten auth login
    ```
  </Tab>
</Tabs>

The CLI examples use `$SANDBOX_NAME` for the sandbox name:

```bash theme={"system"}
export SANDBOX_NAME="hello-world"
```

<Note>
  Sandbox commands are in pre-release. Arguments, flags, and output may change.
</Note>

## Create a sandbox

Creating a sandbox starts an isolated environment from an image. The new
sandbox gets a name, an image, memory (which also sets its CPU allocation), and
a region. Name, image, memory, and region can't change after creation.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const sandbox = await client.create({
      name: "hello-world",
      labels: { purpose: "hello-world" },
    });
    console.log(sandbox.name);
    ```

    The call waits until the sandbox is ready. To reuse a live sandbox with the
    same name, set `createIfNotExists: true`.
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        sandbox = client.create(name="hello-world", labels={"purpose": "hello-world"})
        print(sandbox.name)
    ```

    The call waits until the sandbox is ready. To reuse a live sandbox with the
    same name, pass `create_if_not_exists=True`.
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        sb, err := client.Create(ctx, sandbox.CreateOptions{
            Name:   "hello-world",
            Labels: map[string]string{"purpose": "hello-world"},
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(sb.Name())
    }
    ```

    The call waits until the sandbox is ready. To reuse a live sandbox with the
    same name, set `CreateIfNotExists: true`.
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten sandbox create --name "$SANDBOX_NAME" --label purpose=hello-world
    ```

    The command waits until the sandbox is ready, then prints its record with
    status `DEPLOYED`, so you can [run commands](#run-commands-in-a-sandbox) in
    it right away. Without flags, the sandbox uses **Base Image**, 4096 MiB,
    and the closest region. Change them with `--image`, `--memory`, or
    `--region`:

    ```bash theme={"system"}
    baseten sandbox create --name "$SANDBOX_NAME" --image baseten/base-image:latest --memory 8192 --region us-was-1
    ```

    To reuse a live sandbox with the same name instead of failing on a name
    conflict, add `--if-not-exists`:

    ```bash theme={"system"}
    baseten sandbox create --name "$SANDBOX_NAME" --if-not-exists
    ```
  </Tab>

  <Tab title="UI">
    **To create a sandbox**:

    1. Sign in to your workspace at [app.baseten.co](https://app.baseten.co)
       and choose **Sandboxes** in the sidebar.
    2. Choose **Create sandbox**.
    3. Choose **Base Image**, or another [image](/sandboxes/manage-images) with the
       software your task needs.
    4. Enter a **Name**, for example, `hello-world`.
    5. For **Memory (MiB)**, keep the default or enter the memory the workload
       needs. More memory also means more CPU.
    6. Choose a **Region**.
    7. Optionally, for **Time to live**, choose how long the sandbox can run
       before Baseten deletes it.
    8. If the form shows **Team**, choose the team that should own the sandbox.
    9. Optionally, [add environment variables](#set-environment-variables).
    10. Choose **Create sandbox**.

    If the form reports an error, check the validation message next to each
    field. For common causes, see [Handle a failed command](#handle-a-failed-command).
  </Tab>
</Tabs>

## Get a sandbox

A sandbox's record shows its configuration, URL, and current status.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const info = await client.getInfo({ name: "hello-world" });
    console.log(info.status);
    ```

    To work with the sandbox, not just its record, use
    `client.get({ name: "hello-world" })`.
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        info = client.get_info(name="hello-world")
        print(info.status)
    ```

    To work with the sandbox, not just its record, use
    `sandbox = client.get(name="hello-world")`.
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        info, err := client.GetInfo(ctx, sandbox.GetInfoOptions{Name: "hello-world"})
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(info.Status)
    }
    ```

    To work with the sandbox, not just its record, use
    `client.Get(ctx, sandbox.GetOptions{Name: "hello-world"})`.
  </Tab>

  <Tab title="CLI">
    <CodeGroup>
      ```bash Command theme={"system"}
      baseten sandbox describe --name "$SANDBOX_NAME"
      ```

      ```txt Output theme={"system"}
      Name:         hello-world
      Status:       DEPLOYED
      URL:          <SANDBOX_URL>
      Image:        baseten/base-image:latest
      Memory:       4096 MB
      Region:       us-was-1
      Labels:       purpose=hello-world
      Ports:        8080
      Created:      2026-10-07T17:28:19Z
      ```
    </CodeGroup>

    To print only one field, add `--jq`:

    <CodeGroup>
      ```bash Command theme={"system"}
      baseten sandbox describe --name "$SANDBOX_NAME" --jq '.status'
      ```

      ```txt Output theme={"system"}
      "DEPLOYED"
      ```
    </CodeGroup>
  </Tab>

  <Tab title="UI">
    In **Sandboxes**, choose the sandbox. Its detail page shows the same
    status.
  </Tab>
</Tabs>

If the status is `FAILED`, inspect the sandbox's [logs](/sandboxes/logs)
before retrying. For the deployment and deletion statuses, see
[Lifecycle and expiration](/sandboxes/lifecycle).

## Run commands in a sandbox

Run a single command in a deployed sandbox. Its output streams to your
terminal, and its exit code becomes the command's exit code.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const sandbox = await client.get({ name: "hello-world" });
    const result = await sandbox.process.exec({
      command: "python3 --version",
      waitForCompletion: true,
    });
    console.log(result.stdout, result.exitCode);
    ```

    To set extra environment variables for one command, set `env`:

    ```typescript theme={"system"}
    const result = await sandbox.process.exec({
      command: "python3 worker.py",
      env: { LOG_LEVEL: "debug" },
      waitForCompletion: true,
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        sandbox = client.get(name="hello-world")
        result = sandbox.process.exec(command="python3 --version", wait_for_completion=True)
        print(result.stdout, result.exit_code)
    ```

    To set extra environment variables for one command, pass `env`:

    ```python theme={"system"}
    result = sandbox.process.exec(
        command="python3 worker.py",
        env={"LOG_LEVEL": "debug"},
        wait_for_completion=True,
    )
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        sb, err := client.Get(ctx, sandbox.GetOptions{Name: "hello-world"})
        if err != nil {
            log.Fatal(err)
        }
        process, err := sb.Process().Exec(ctx, sandbox.ProcessExecOptions{
            Command:           "python3 --version",
            WaitForCompletion: true,
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(process.Stdout, process.ExitCode)
    }
    ```

    To set extra environment variables for one command, set `Env`:

    ```go theme={"system"}
    process, err := sb.Process().Exec(ctx, sandbox.ProcessExecOptions{
        Command:           "python3 worker.py",
        Env:               map[string]string{"LOG_LEVEL": "debug"},
        WaitForCompletion: true,
    })
    ```
  </Tab>

  <Tab title="CLI">
    Put the command after `--`:

    <CodeGroup>
      ```bash Command theme={"system"}
      baseten sandbox exec --name "$SANDBOX_NAME" -- python3 --version
      ```

      ```txt Output theme={"system"}
      Python 3.12.15
      ```
    </CodeGroup>

    To set extra environment variables for one command, add `--env KEY=VALUE`
    before `--`:

    ```bash theme={"system"}
    baseten sandbox exec --name "$SANDBOX_NAME" --env LOG_LEVEL=debug -- python3 worker.py
    ```

    To open an interactive terminal in the sandbox, like SSH, run:

    ```bash theme={"system"}
    baseten sandbox connect --name "$SANDBOX_NAME"
    ```

    Press **Ctrl+D** to disconnect.
  </Tab>
</Tabs>

## Set environment variables

Environment variables pass configuration, such as an output format or log
level, to the processes in a sandbox, so one image can serve different tasks.
Set them at creation. Names begin with a letter or underscore and contain only
letters, digits, and underscores. Don't put your Baseten API key in a
sandbox's environment.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const sandbox = await client.create({
      name: "hello-world",
      envs: { OUTPUT_FORMAT: { value: "json" } },
    });
    console.log(sandbox.name);
    ```

    Values are secret by default and come back masked.
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient, SandboxEnvValue

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        sandbox = client.create(
            name="hello-world",
            envs={"OUTPUT_FORMAT": SandboxEnvValue(value="json")},
        )
        print(sandbox.name)
    ```

    Values are secret by default and come back masked.
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        sb, err := client.Create(ctx, sandbox.CreateOptions{
            Name: "hello-world",
            Envs: map[string]sandbox.EnvValue{"OUTPUT_FORMAT": {Value: "json"}},
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(sb.Name())
    }
    ```

    Values are secret by default and come back masked.
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten sandbox create --name "$SANDBOX_NAME" --env OUTPUT_FORMAT=json
    ```

    `baseten sandbox describe` masks each value. To verify a value after
    deployment, read the variable inside the sandbox:

    <CodeGroup>
      ```bash Command theme={"system"}
      baseten sandbox exec --name "$SANDBOX_NAME" -- 'echo $OUTPUT_FORMAT'
      ```

      ```txt Output theme={"system"}
      json
      ```
    </CodeGroup>
  </Tab>

  <Tab title="UI">
    **To set environment variables**:

    1. In **Sandboxes**, choose **Create sandbox**, then choose an image.
    2. Under **Environment variables**, choose **Add variable**.
    3. Type a name and value, such as `OUTPUT_FORMAT` and `json`.
    4. Repeat steps 2 and 3 for each additional variable.
  </Tab>
</Tabs>

## List sandboxes

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    for await (const sandbox of client.list({ query: "hello" })) {
      console.log(sandbox.name, sandbox.status);
    }
    ```

    Filter by status with `statuses`, such as `statuses: ["DEPLOYED"]`.
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        for sandbox in client.list(query="hello"):
            print(sandbox.name, sandbox.status)
    ```

    Filter by status with `statuses`, such as `statuses=["DEPLOYED"]`.
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        for info, err := range client.List(ctx, sandbox.ListOptions{Query: "hello"}) {
            if err != nil {
                log.Fatal(err)
            }
            fmt.Println(info.Name, info.Status)
        }
    }
    ```

    Filter by status with `Statuses`, such as
    `Statuses: []sandbox.Status{sandbox.StatusDeployed}`.
  </Tab>

  <Tab title="CLI">
    <CodeGroup>
      ```bash Command theme={"system"}
      baseten sandbox list
      ```

      ```txt Output theme={"system"}
      NAME         STATUS    REGION    CREATED
      hello-world  DEPLOYED  us-was-1  2026-10-07T17:28:19Z
      ```
    </CodeGroup>

    The output lists every sandbox in the team, except terminated ones. Narrow
    it with `--status` or search names and labels with `--query`:

    ```bash theme={"system"}
    baseten sandbox list --status deployed
    baseten sandbox list --query docs
    ```
  </Tab>
</Tabs>

## Manage a sandbox's labels

Labels help you find and group sandboxes, such as marking every sandbox an
agent creates with `purpose=agent`. This example changes the `purpose` label
from `hello-world` to `hello-universe`:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    const updated = await client.update({
      name: "hello-world",
      labels: { purpose: "hello-universe" },
    });
    console.log(updated.labels);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        info = client.update(name="hello-world", labels={"purpose": "hello-universe"})
        print(info.labels)
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "fmt"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        updated, err := client.Update(ctx, sandbox.UpdateOptions{
            Name:   "hello-world",
            Labels: map[string]string{"purpose": "hello-universe"},
        })
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(updated.Labels)
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten sandbox update --name "$SANDBOX_NAME" --label purpose=hello-universe
    ```

    The command prints the updated sandbox.
  </Tab>

  <Tab title="UI">
    **To change a sandbox's labels**:

    1. In **Sandboxes**, choose the sandbox.
    2. Choose **Sandbox settings**.
    3. Under **Labels**, add, change, or remove labels.
    4. Choose **Save changes**.

    **Sandbox settings** is unavailable while a sandbox is deleting or
    archived.
  </Tab>
</Tabs>

A supplied set of labels replaces all previous labels, so include every one you
want to keep. Omitted fields leave their values unchanged. You can also update
environment variables with `envs`.

The name, image, memory, and region can't change after creation. To use a
different image, [create a new sandbox](#create-a-sandbox).

## Delete a sandbox

Deletion stops the sandbox's processes and permanently removes its files, so
save the files you need first.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({ apiKey: process.env.BASETEN_API_KEY! });
    await client.delete({ name: "hello-world" });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    with SandboxClient(api_key=os.environ["BASETEN_API_KEY"]) as client:
        client.delete(name="hello-world")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "context"
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        ctx := context.Background()
        client, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
        })
        if err != nil {
            log.Fatal(err)
        }
        if _, err := client.Delete(ctx, sandbox.DeleteOptions{Name: "hello-world"}); err != nil {
            log.Fatal(err)
        }
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten sandbox delete --name "$SANDBOX_NAME"
    ```

    Confirm when prompted. In scripts, add `--yes` to skip the prompt:

    ```bash theme={"system"}
    baseten sandbox delete --name "$SANDBOX_NAME" --yes
    ```
  </Tab>

  <Tab title="UI">
    **To delete a sandbox**:

    1. In **Sandboxes**, choose the sandbox.
    2. Choose **Delete sandbox**.
    3. Type the sandbox's name to confirm, then choose **Delete sandbox**.
  </Tab>
</Tabs>

Deletion continues after the call returns. Stop sending work to the sandbox,
then [describe the sandbox](#get-a-sandbox) until its status is `TERMINATED`.
The sandbox reports `TERMINATED` for a few minutes, and then it's no longer
found. Don't reuse the name until deletion completes.

## Work in a team

Sandbox commands act in your team. If you belong to more than one, name the
team on each call:

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={"system"}
    import { SandboxClient } from "@basetenlabs/sandbox";

    const client = new SandboxClient({
      apiKey: process.env.BASETEN_API_KEY!,
      teamId: "<TEAM_ID>",
    });
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import os

    from baseten.sandbox import SandboxClient

    client = SandboxClient(api_key=os.environ["BASETEN_API_KEY"], team_id="<TEAM_ID>")
    ```
  </Tab>

  <Tab title="Go">
    ```go theme={"system"}
    package main

    import (
        "log"
        "os"

        "github.com/basetenlabs/baseten-go/sandbox"
    )

    func main() {
        _, err := sandbox.NewClient(sandbox.ClientOptions{
            APIKey: os.Getenv("BASETEN_API_KEY"),
            TeamID: "<TEAM_ID>",
        })
        if err != nil {
            log.Fatal(err)
        }
    }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={"system"}
    baseten sandbox list --team <TEAM_ID>
    ```

    Run `baseten org team list` to see your teams, and use the same team
    throughout a workflow.
  </Tab>
</Tabs>

## Handle a failed command

Use the error message to choose the next action:

| Error | Check |
| - | - |
| Not authenticated | Run `baseten auth status`, then `baseten auth login` or set `BASETEN_API_KEY`. |
| Permission denied | Sandboxes access for your organization and your permissions in the team. |
| Invalid name on create | The name must be 1 to 49 lowercase letters, digits, or hyphens, and start and end with a letter or digit. |
| Name conflict on create | Choose an unused name, or add `--if-not-exists` to reuse the live sandbox. |
| Conflict on delete | The sandbox is in use or conflicts with the request. Retry after its current operation finishes. |
| Invalid update | An immutable field or an invalid value. |
| Team required | Your account can access more than one team. Add the team as shown in [Work in a team](#work-in-a-team). |

## Next steps

<CardGroup cols={2}>
  <Card title="Sandbox lifecycle and expiration" icon="clock" href="/sandboxes/lifecycle">
    Track deployment status and set an expiration policy.
  </Card>

  <Card title="Manage sandbox images" icon="box" href="/sandboxes/manage-images">
    Choose a built-in image, or create and version your own.
  </Card>
</CardGroup>
