Hanzo
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.

Nouns

E2BHanzo
Sandbox.create()POST /v1/sandbox/lease
The sandbox id you reconnect withid 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 / timeoutttlSec on the lease
Templateclassexec, dev, desktop or android — with image to override its default
Persistent template dataproject 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 streamPOST /v1/sandbox/{id}/screen/ticket, then /v1/sandbox/{id}/screen/ws
Listing your sandboxesGET /v1/sandbox

dev and desktop require a project; exec does not.

The call

E2B:

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:

# 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:

curl -X POST https://api.hanzo.ai/v1/exec \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"lang":"python","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 image on the lease overrides the class default, so you bring a container image you built elsewhere. 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.

How is this guide?

On this page