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

# Gemini Managed Agents

> Run the file and shell tools of a Gemini API managed agent inside your own E2B sandbox.

With a [self-hosted environment](https://aistudio.google.com/docs/agent-environment#self-hosted-environments), a Managed Agent on the Gemini API runs its file and shell tools in a machine you control instead of on Google's. Gemini plans the work at Google. A small worker inside the sandbox, `gemini-api environments connect`, calls out to Google over HTTPS, picks up each tool call and runs it locally. Nothing connects in.

The `gemini-e2b` template is that machine, ready to go. One command starts it:

```bash theme={"theme":"css-variables"}
e2b sandbox create gemini-e2b --timeout 3600
```

From there you can work either way:

* [**In the terminal**](#option-1-in-the-terminal), with the `e2b` and `gemini-api` CLIs.
* [**In the web workbench**](#option-2-in-the-web-workbench) the sandbox serves: open the link the terminal prints, paste your key, and chat with the agent.

## What the template does for you

* **One command to start.** `gemini-e2b` is a prebuilt sandbox: the `gemini-api` CLI is installed, pinned and checksum-verified, next to `git`, `python3` and `node`. There is no image to build and no repository to clone.
* **The agent cannot read your keys.** Both Gemini keys stay in [E2B Secrets](https://docs.e2b.dev/secrets/inject.md). The sandbox only holds the word `placeholder`, and E2B swaps in the real key on requests to the Gemini API. Because the agent's shell runs in the same sandbox as the worker, a key passed in as an environment variable would be readable by the agent. Here there is nothing to read.
* **A web workbench.** A browser UI on port `8000` stores your key, connects the worker and lets you chat with the agent, with every tool call and every file it writes shown live.
* **A private link.** The workbench is locked by a per-sandbox access token that only the sandbox terminal prints, so only you can open it.

## Before you begin

* The [E2B CLI](https://docs.e2b.dev/cli.md), logged in with `e2b auth login`, and an [E2B API key](https://e2b.dev/dashboard?tab=keys).
* A [Gemini API key](https://aistudio.google.com/apikey).
* For option 1, both keys in your shell, plus `jq` and the `gemini-api` CLI on your machine:

```bash theme={"theme":"css-variables"}
export E2B_API_KEY=...
export GEMINI_API_KEY=...
curl -fsSL https://raw.githubusercontent.com/google-gemini/gemini-api-cli/main/scripts/install.sh | bash
```

## Option 1: in the terminal

**1. Create the sandbox**

```bash theme={"theme":"css-variables"}
export SBX=$(e2b sandbox create gemini-e2b --detach --timeout 3600 | grep -oE '^[a-z0-9]{20,}$')
```

The worker only makes outbound calls, so [auto-resume](https://docs.e2b.dev/sandbox/auto-resume.md) cannot wake a paused sandbox for it. Give the sandbox a timeout long enough for the session: the default five minutes would stop the worker mid-task.

**2. Create the environment and store the keys**

Create a self-hosted environment, then put your Gemini API key and the environment key into [E2B Secrets](https://docs.e2b.dev/secrets/create.md):

```bash theme={"theme":"css-variables"}
RESP=$(curl -fsS -X POST "https://generativelanguage.googleapis.com/v1beta/environments" \
  -H "x-goog-api-key: $GEMINI_API_KEY" -H "Content-Type: application/json" \
  -d '{"mode": "self_hosted"}')
export GEMINI_ENVIRONMENT_ID=$(jq -r .id <<<"$RESP")

jq -n --arg name "gemini-key-$GEMINI_ENVIRONMENT_ID" --arg value "$GEMINI_API_KEY" '{name: $name, value: $value}' |
  curl -fsS -o /dev/null -X POST "https://api.e2b.app/secrets" \
    -H "X-API-Key: $E2B_API_KEY" -H "Content-Type: application/json" -d @-
jq --arg name "gemini-env-$GEMINI_ENVIRONMENT_ID" '{name: $name, value: .environment_key}' <<<"$RESP" |
  curl -fsS -o /dev/null -X POST "https://api.e2b.app/secrets" \
    -H "X-API-Key: $E2B_API_KEY" -H "Content-Type: application/json" -d @-
unset RESP
```

The environment key is returned only once, so it goes from the response straight into the vault and is never printed. The ID is a bare UUID: use it as is, without an `environments/` prefix.

**3. Tell the sandbox to inject them**

Add a [network rule](https://docs.e2b.dev/secrets/inject.md) to the sandbox. E2B then adds both keys to every request the sandbox sends to the Gemini API:

```bash theme={"theme":"css-variables"}
jq -n --arg id "$GEMINI_ENVIRONMENT_ID" '{rules: {"generativelanguage.googleapis.com": [{transform: {headers: {
  "x-goog-api-key": "${e2b.secrets.gemini-key-\($id)}",
  "x-goog-environment-key": "${e2b.secrets.gemini-env-\($id)}"
}}}]}}' |
  curl -fsS -o /dev/null -X PUT "https://api.e2b.app/sandboxes/$SBX/network" \
    -H "X-API-Key: $E2B_API_KEY" -H "Content-Type: application/json" -d @-
```

**4. Connect the worker**

Start the worker with a placeholder instead of the key:

```bash theme={"theme":"css-variables"}
e2b sandbox exec -b -e GEMINI_ENVIRONMENT_KEY=placeholder "$SBX" \
  bash -c 'gemini-api environments connect > /tmp/worker.log 2>&1'
e2b sandbox exec "$SBX" cat /tmp/worker.log
```

The log should end with `Worker ... polling (maxConcurrent=4)`.

**5. Give the agent a task**

```bash theme={"theme":"css-variables"}
gemini-api agent run "Create hello.txt and run uname -a in bash." \
  --agent antigravity-preview-09-2026 \
  --body "{\"environment\": \"$GEMINI_ENVIRONMENT_ID\"}"
```

The agent reports `Linux e2b.local ...`: its shell ran inside the sandbox. Check the file it wrote:

```bash theme={"theme":"css-variables"}
e2b sandbox exec "$SBX" cat /home/user/workspace/hello.txt
```

**6. Clean up**

```bash theme={"theme":"css-variables"}
e2b sandbox kill "$SBX"
gemini-api environments delete "$GEMINI_ENVIRONMENT_ID"
for name in "gemini-key-$GEMINI_ENVIRONMENT_ID" "gemini-env-$GEMINI_ENVIRONMENT_ID"; do
  curl -fsS -o /dev/null -X DELETE "https://api.e2b.app/secrets/$name" -H "X-API-Key: $E2B_API_KEY"
done
```

## Option 2: in the web workbench

**1. Create the sandbox and open the link**

```bash theme={"theme":"css-variables"}
e2b sandbox create gemini-e2b --timeout 3600
```

The terminal prints a link to the workbench:

```text theme={"theme":"css-variables"}
  GEMINI API SELF-HOSTED ENVIRONMENT
  OPEN THE WORKBENCH: https://8000-<sandbox-id>.e2b.app/#token=<access-token>
```

Run `demo-url` in the sandbox terminal to print it again.

**2. Store your Gemini key in the vault**

On the first visit, paste your Gemini API key and an E2B API key. The workbench then does what steps 2 and 3 of option 1 do by hand: it creates the environment, stores both Gemini keys in E2B Secrets and adds the network rule to its own sandbox.

The E2B API key is used for those two calls and then dropped. Neither key is written to disk or kept in the sandbox.

**3. Connect the worker**

The vault check calls Google with a placeholder key and turns green only if E2B injected the real one. Then click **Connect** to start the worker.

**4. Give the agent a task**

Pick a suggestion or type a task. The answer streams into the chat, **Activity** shows every tool call the worker runs, and **Files** shows what the agent wrote to `/home/user/workspace`. Follow-up messages continue the same conversation.

### Who can open the workbench

A sandbox's port URL is public, so the workbench is locked by an access token:

* The token is generated inside the sandbox, one per sandbox, and is printed only in the sandbox terminal. Opening the terminal takes your E2B login, so only you can get the link.
* The token travels in the part of the link after `#`, which the browser never sends to a server. The page keeps it for the tab and removes it from the address bar.
* Every API call of the workbench without the right token is rejected with `401`. Someone with the bare URL sees the page but cannot use it.

Treat the link like a password: anyone you give it to can run the agent in your sandbox.

## From your own code

The same setup with the SDK: store both keys with [`Secret.create`](https://docs.e2b.dev/secrets/create) and create the sandbox with the rule that injects them.

```typescript JavaScript & TypeScript theme={"theme":"css-variables"}
import { Sandbox, Secret } from 'e2b'

const GEMINI_HOST = 'generativelanguage.googleapis.com'

// 1. A self-hosted environment. Its key is returned once: put it straight into the vault.
const env = await fetch(`https://${GEMINI_HOST}/v1beta/environments`, {
  method: 'POST',
  headers: { 'x-goog-api-key': process.env.GEMINI_API_KEY!, 'content-type': 'application/json' },
  body: JSON.stringify({ mode: 'self_hosted' }),
}).then((res) => res.json())

// 2. Both keys into E2B Secrets.
await Secret.create(`gemini-key-${env.id}`, process.env.GEMINI_API_KEY!)
await Secret.create(`gemini-env-${env.id}`, env.environment_key)

// 3. A sandbox that injects them into requests to the Gemini API.
const sandbox = await Sandbox.create('gemini-e2b', {
  timeoutMs: 60 * 60_000,
  network: {
    rules: {
      [GEMINI_HOST]: [
        {
          transform: {
            headers: {
              'x-goog-api-key': Secret.fill(`gemini-key-${env.id}`),
              'x-goog-environment-key': Secret.fill(`gemini-env-${env.id}`),
            },
          },
        },
      ],
    },
  },
})

// 4. The worker gets a placeholder. The CLI refuses an empty key.
await sandbox.commands.run('gemini-api environments connect', {
  background: true,
  envs: { GEMINI_ENVIRONMENT_KEY: 'placeholder' },
})
```

```python Python theme={"theme":"css-variables"}
import os

import requests
from e2b import Sandbox, Secret

GEMINI_HOST = "generativelanguage.googleapis.com"

# 1. A self-hosted environment. Its key is returned once: put it straight into the vault.
env = requests.post(
    f"https://{GEMINI_HOST}/v1beta/environments",
    headers={"x-goog-api-key": os.environ["GEMINI_API_KEY"]},
    json={"mode": "self_hosted"},
).json()

# 2. Both keys into E2B Secrets.
Secret.create(f"gemini-key-{env['id']}", os.environ["GEMINI_API_KEY"])
Secret.create(f"gemini-env-{env['id']}", env["environment_key"])

# 3. A sandbox that injects them into requests to the Gemini API.
sandbox = Sandbox.create(
    "gemini-e2b",
    timeout=60 * 60,
    network={
        "rules": {
            GEMINI_HOST: [
                {
                    "transform": {
                        "headers": {
                            "x-goog-api-key": Secret.fill(f"gemini-key-{env['id']}"),
                            "x-goog-environment-key": Secret.fill(f"gemini-env-{env['id']}"),
                        },
                    },
                },
            ],
        },
    },
)

# 4. The worker gets a placeholder. The CLI refuses an empty key.
sandbox.commands.run(
    "gemini-api environments connect",
    background=True,
    envs={"GEMINI_ENVIRONMENT_KEY": "placeholder"},
)
```

> [!NOTE]
>
> Injection is keyed on the host, so any process in the sandbox can still make authenticated calls to the Gemini API, even though it cannot read the key. Other outbound traffic stays open so the agent can install packages. To restrict it, add `allowOut` and `denyOut` as described in [internet access](https://docs.e2b.dev/network/internet-access.md).
