# E2B

URL: https://docs.hanzo.ai/docs/guides/migrate/e2b

> E2B leases isolated sandboxes for code an agent wrote. Here that is /v1/sandbox, with /v1/exec for the one-shot snippet case.

E2B leases an isolated computer, runs code in it, and gives you its filesystem.
`/v1/sandbox` (19 operations) is the same thing: lease, run, read, write, end.
`POST /v1/exec` is the short path — one snippet, one answer, no lease to manage.

## Start here

Mint a key, run one whole program, and take a lease only when the work has to
outlive its own call.

```bash
# 1. mint a key — sk- belongs on a server, pk- is safe in a browser
curl -sS -X POST https://api.hanzo.ai/v1/account/keys \
  -H "Authorization: Bearer $HANZO_SESSION" \
  -H 'Content-Type: application/json' \
  -d '{"type":"secret"}'

# 2. run a program in a throwaway sandbox — no lease to manage, nothing to clean up
curl -sS -X POST https://api.hanzo.ai/v1/exec \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"lang":"py","code":"print(1 + 1)"}'

# 3. hold the machine instead — dev attaches the project's disk, exec keeps nothing
curl -sS -X POST https://api.hanzo.ai/v1/sandbox/lease \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"class":"dev","project":"demo","ttlSec":900}'
```

Step 2 answers `stdout`, `stderr`, the files the program wrote, and the
`session_id` of the sandbox it leased for you — send that id with the next run
and it sees the same filesystem, which is the whole of `runCode()`. Step 3 is
that computer held open under a name you keep: `id` is the argument `run`,
`read`, `write` and `end` all take, and `workdir` is what a relative path in
them resolves against.

### Core capabilities

| Capability         | What it does                                                                                                                                                              | Operations |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `/v1/sandbox`      | Leases a real computer with its own filesystem, runs commands in it, reads and writes its files, and ends it. Terminal and screen are WebSockets behind a ticket.         | 19         |
| `/v1/exec`         | Runs one whole program in a throwaway sandbox across thirteen languages, returns what it printed and what it wrote, and keeps the filesystem when you reuse `session_id`. | 4          |
| `/v1/account/keys` | Mints, lists and revokes the sk- and pk- keys every call above authenticates with. Minting a class again rotates it in one step.                                          | 3          |

## Nouns

| E2B                                 | Hanzo                                                                                      |
| ----------------------------------- | ------------------------------------------------------------------------------------------ |
| `Sandbox.create()`                  | `POST /v1/sandbox/lease`                                                                   |
| The sandbox id you reconnect with   | `id` in the lease answer, passed in every later body                                       |
| `sandbox.commands.run(cmd)`         | `POST /v1/sandbox/run`                                                                     |
| `sandbox.files.write(path, bytes)`  | `POST /v1/sandbox/write`, `data` base64                                                    |
| `sandbox.files.read(path)`          | `POST /v1/sandbox/read`, `data` base64, or `entries` for a directory                       |
| `sandbox.kill()`                    | `POST /v1/sandbox/end`                                                                     |
| `timeoutMs` / `timeout`             | `ttlSec` on the lease                                                                      |
| Template                            | `class` — `exec`, `dev`, `desktop` or `android` — a closed set, not an image you build     |
| Persistent template data            | `project` on the lease, which names the volume the work persists on                        |
| `@e2b/code-interpreter` `runCode()` | `POST /v1/exec` with `lang` and `code`                                                     |
| Interactive terminal (PTY)          | `POST /v1/sandbox/{id}/terminal/ticket`, then the socket at `/v1/sandbox/{id}/terminal/ws` |
| Desktop stream                      | `POST /v1/sandbox/{id}/screen/ticket`, then `/v1/sandbox/{id}/screen/ws`                   |
| Listing your sandboxes              | `GET /v1/sandbox`                                                                          |

`dev` and `desktop` require a `project`; `exec` does not.

## The call

E2B:

```python
from e2b import Sandbox   # E2B_API_KEY in the environment

with Sandbox.create(timeout=60) as sandbox:
    sandbox.files.write("/home/user/main.py", b"print(1 + 1)")
    result = sandbox.commands.run("python /home/user/main.py")
    print(result.stdout)
```

Hanzo:

```bash
# 1. Lease. The answer carries id, class, runtime, status and workdir.
SB=$(curl -sS -X POST https://api.hanzo.ai/v1/sandbox/lease \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"class":"exec","ttlSec":60}' | jq -r .id)

# 2. Write. `data` is base64.
curl -sS -X POST https://api.hanzo.ai/v1/sandbox/write \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"id\":\"$SB\",\"path\":\"main.py\",\"data\":\"$(printf 'print(1 + 1)' | base64 -w0)\"}"

# 3. Run. Answers exitCode, stdout, stderr.
curl -sS -X POST https://api.hanzo.ai/v1/sandbox/run \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"id\":\"$SB\",\"command\":\"python main.py\"}"

# 4. Release it.
curl -sS -X POST https://api.hanzo.ai/v1/sandbox/end \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "{\"id\":\"$SB\"}"
```

If all you want is one snippet, skip the lease entirely:

```bash
curl -X POST https://api.hanzo.ai/v1/exec \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"lang":"py","code":"print(1 + 1)"}'
```

A non-zero exit is a 200 carrying a failed program. "The tests failed" and "the
sandbox is broken" are different facts and they get different status codes.

Pass a `session` name to `run` and the command's output is appended to that
session's live log as it is produced, so a long agentic run can be watched
rather than waited on.

## What does not carry

**No snapshots and no forking.** E2B pauses a sandbox with its full state and
boots new ones from that state. Leasing the same `id` again resumes a lease that
is still running, which is reattachment, not restoration. When a lease ends,
the sandbox is gone; `purge` on the end call decides whether the project volume
goes with it.

**No template build.** E2B builds a template from your Dockerfile and caches it.
Here `class` picks from a closed set of four, and the image behind the one you
pick is the fleet's. There is no template registry to push to.

**No environment variables on the lease.** The lease body is `class`, `id`,
`project`, `runtime` and `ttlSec` — nothing else. Set variables in the command
you run.

**No lifecycle events, metrics or OTel export.** E2B emits sandbox lifecycle
webhooks and per-sandbox metrics. `GET /v1/sandbox` answers from the org's own
store rather than the cluster, so a sandbox whose pod has died still appears with
the status it was last known to have — deliberate, because a lease you are being
charged for should not vanish from the list.

**No SSH and no git methods.** The terminal is a WebSocket behind a ticket. Git
work is a command you run, like any other.
