PAI-Sandbox provides prebuilt public templates for common agent scenarios: code execution, browser automation, desktop automation, and AI coding. Select a template based on your use case and create a sandbox without additional setup.
Overview
Public templates are prebuilt by the EAS platform and can be used in any sandbox workspace without additional setup. Each template preconfigures the runtime environment for a specific agent scenario, including preinstalled libraries, prestarted services, and preset ports.
Template comparison
|
Agent scenario |
Recommended template |
Key capabilities |
|
Code interpreter / data analysis |
|
Python 3.11 (with numpy, pandas, matplotlib, and sklearn), JavaScript, and TypeScript |
|
Browser automation / web search |
|
Chromium with Playwright and MCP dual-protocol access |
|
Desktop GUI automation / screenshots |
|
Ubuntu 22.04 with remote desktop and UI automation SDK |
|
AI coding (Claude) |
|
Claude CLI with Bailian API access |
|
AI coding (open source) |
|
opencode with opencode server |
|
General-purpose task agent |
|
General-purpose task agent framework with Bailian API access |
Prerequisites
Before you use a public template:
-
On the Template Details page, click SDK Access in the upper-right corner to obtain the E2B_DOMAIN, E2B_API_KEY, and template ID.
NotePublic templates use the sandbox workspace-level token as the E2B_API_KEY. You can set a custom token when creating a sandbox workspace to replace the auto-generated one.
Install the E2B SDK (only versions earlier than v2.25.0 are supported):
pip install "e2b<2.25.0".
code-interpreter
The code-interpreter public template provides a development environment with Python 3.11, JavaScript, and TypeScript. Use the e2b_code_interpreter SDK to run code and manage files.
pip install e2b_code_interpreter
import os
import time
from e2b_code_interpreter import Sandbox
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
sandbox = Sandbox.create(template="code-interpreter", timeout=300000)
# Wait for the sandbox to be ready
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
# Python code execution
execution = sandbox.run_code("print('hello world')")
print(execution.logs)
sandbox.run_code("x = 42")
execution = sandbox.run_code("print(f'x = {x}')")
print(execution.logs.stdout)
# JavaScript
result = sandbox.commands.run("node -e \"console.log('Hello from Node.js')\"")
print(result.stdout)
# TypeScript
result = sandbox.commands.run("tsx -e \"const msg: string = 'Hello from TypeScript'; console.log(msg)\"")
print(result.stdout)
# File operations
entries = sandbox.files.list("/home/user")
for e in entries:
print(f"{e.type}\t{e.name}")
sandbox.files.write("/home/user/hello.txt", "Hello from sandbox")
content = sandbox.files.read("/home/user/hello.txt")
print(content)
browser
The browser public template includes a preinstalled Chromium browser with two access methods: CDP (Chrome DevTools Protocol) on port 8080 and MCP on port 50005. Agents can use these interfaces to automate browser interactions through Playwright or MCP.
Option 1: Playwright
Install Playwright on the client:
pip install playwright
Connect to the sandbox browser via the CDP WebSocket endpoint and use the Playwright API for page navigation, screenshots, and element extraction:
import asyncio
import os
import time
from urllib.parse import quote_plus
import httpx
from e2b import Sandbox
from playwright.async_api import async_playwright
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
GATEWAY_PORT = 8080
sandbox = Sandbox.create(template="browser", timeout=300000)
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
# Authentication headers (required for all requests)
def _auth_headers():
return {
"X-Access-Token": sandbox._envd_access_token,
"E2b-Sandbox-Id": sandbox.sandbox_id,
}
# Get the CDP WebSocket URL
def get_cdp_url():
gateway_host = sandbox.get_host(GATEWAY_PORT)
resp = httpx.get(
f"https://{gateway_host}/json/version",
headers=_auth_headers(), timeout=10, verify=False,
)
resp.raise_for_status()
guid = resp.json()["webSocketDebuggerUrl"].rsplit("/", 1)[-1]
return f"wss://{gateway_host}/devtools/browser/{guid}"
async def search_with_playwright(query: str):
cdp_url = get_cdp_url()
print(f"CDP URL: {cdp_url}")
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(cdp_url, headers=_auth_headers())
if browser.contexts and browser.contexts[0].pages:
page = browser.contexts[0].pages[0]
else:
page = await (await browser.new_context()).new_page()
# Navigate
search_url = f"https://www.bing.com/search?q={quote_plus(query)}"
await page.goto(search_url, wait_until="load", timeout=30000)
print(f"Navigated to: {search_url}")
# Wait for search results to render
try:
await page.wait_for_selector("li.b_algo", timeout=10000)
except Exception:
await page.wait_for_timeout(3000)
# Take a screenshot
with open("screenshot.png", "wb") as f:
f.write(await page.screenshot(full_page=True))
print("Screenshot saved: screenshot.png")
# Extract search results
results = await page.evaluate("""() => {
const items = document.querySelectorAll('li.b_algo h2 a');
return Array.from(items).slice(0, 5).map(a => ({
title: a.textContent || '',
url: a.href || ''
}));
}""")
await browser.close()
return results
if __name__ == "__main__":
results = asyncio.run(search_with_playwright("Python latest release"))
for i, r in enumerate(results, 1):
print(f" {i}. {r['title']} — {r['url']}")
Option 2: MCP
Install MCP on the client:
pip install mcp
Use semantic browser tools through the MCP protocol without managing page objects directly:
import asyncio
import os
import time
from urllib.parse import quote_plus
import httpx
from e2b import Sandbox
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamable_http_client
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
MCP_PORT = 50005
sandbox = Sandbox.create(template="browser", timeout=300000)
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
def _auth_headers():
return {
"X-Access-Token": sandbox._envd_access_token,
"E2b-Sandbox-Id": sandbox.sandbox_id,
}
def get_mcp_url():
return f"https://{sandbox.get_host(MCP_PORT)}/mcp"
async def search_with_mcp(query: str):
mcp_url = get_mcp_url()
print(f"MCP URL: {mcp_url}")
client = httpx.AsyncClient(headers=_auth_headers(), verify=False)
async with client:
async with streamable_http_client(mcp_url, http_client=client) as (
read_stream, write_stream, _,
):
async with ClientSession(read_stream, write_stream) as session:
await session.initialize()
print("MCP session initialized")
# Navigate
search_url = f"https://www.bing.com/search?q={quote_plus(query)}"
await session.call_tool("browser_navigate", {"url": search_url})
print(f"Navigated to: {search_url}")
# Wait for page to load
await session.call_tool("browser_wait_for", {"time": 2})
# Get page snapshot
snap = await session.call_tool("browser_snapshot", {})
result_text = " ".join(
getattr(item, "text", "") for item in snap.content
)
return result_text
if __name__ == "__main__":
text = asyncio.run(search_with_mcp("Python latest release"))
print(f"Result:\n{text}")
desktop
The desktop public template runs Ubuntu 22.04. Use the e2b_desktop SDK to automate UI operations in the sandbox.
pip install e2b_desktop
import os
import time
from e2b_desktop import Sandbox
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
create_kwargs = {
"timeout": 3600,
"resolution": (1920, 1080),
"dpi": 96,
"template": "desktop",
}
desktop = Sandbox.create(
**create_kwargs,
headers={"X-Sandbox-Create-Timeout": "100"},
)
print(f"Waiting for sandbox {desktop.sandbox_id} to be ready...")
for _ in range(30):
try:
if desktop.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
# UI operations
print("Moving mouse to (100, 100)...")
desktop.move_mouse(100, 100)
time.sleep(0.5)
print("Left clicking...")
desktop.left_click()
time.sleep(0.5)
print("Typing 'Hello'...")
desktop.write("Hello")
time.sleep(0.5)
print("Pressing Enter...")
desktop.press("enter")
time.sleep(0.5)
print("Opening browser to example.com...")
desktop.open("https://example.com")
time.sleep(2)
# Take a screenshot and download it
print("Taking screenshot...")
screenshot = desktop.screenshot("bytes")
if screenshot:
screenshot_path = "/tmp/desktop_screenshot.png"
with open(screenshot_path, "wb") as f:
f.write(screenshot)
print(f"Screenshot saved to {screenshot_path}")
else:
print("Screenshot returned empty")
On the sandbox details page, click Open VNC to connect to the sandbox GUI remotely.
claude-code
The claude-code public template includes the Claude CLI. Create a sandbox from this template and set the ANTHROPIC environment variables to run Claude for coding tasks in isolation.
import os
import time
from e2b import Sandbox
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
sandbox = Sandbox.create(
template="claude-code",
timeout=300000,
envs={
"ANTHROPIC_AUTH_TOKEN": "<YOUR-TOKEN>",
"ANTHROPIC_BASE_URL": "<YOUR-BASE-URL>",
"ANTHROPIC_MODEL": "<YOUR-MODEL>",
},
)
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
result = sandbox.commands.run(
'claude -p "Write a Python program to calculate Fibonacci numbers"',
)
print(result.stdout)
opencode
The opencode public template ships with opencode and starts the opencode server on port 4096.
The opencode configuration file /root/.config/opencode/opencode.json includes preset model configurations for Alibaba Cloud Bailian Token Plan, Coding Plan, and pay-as-you-go mode. When creating a sandbox, provide the key through the BAILIAN-TOKEN-PLAN-KEY, BAILIAN-CODING-PLAN-KEY, or BAILIAN-API-KEY environment variable. You can then use the E2B SDK or HTTP requests to run opencode for coding tasks in the sandbox.
import os
import time
import requests
from e2b import Sandbox
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
OPENCODE_SERVER_PORT = 4096
sandbox = Sandbox.create(
template="opencode",
timeout=300000,
envs={"BAILIAN-API-KEY": os.getenv("BAILIAN-API-KEY")},
)
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
# Call the opencode CLI via the e2b SDK
result = sandbox.commands.run(
'opencode run --model=bailian-payg/qwen3.7-max "Write a Python program to calculate Fibonacci numbers"',
timeout=120,
)
print(result.stdout)
# Call via the opencode server API
BASE_URL = f"https://{sandbox.get_host(4096)}"
H = {"X-Access-Token": getattr(sandbox, '_SandboxBase__envd_access_token', None)}
# Set the API key (the opencode server does not automatically parse {env:...} templates)
api_key = sandbox.commands.run("printenv BAILIAN-API-KEY").stdout.strip()
requests.patch(f"{BASE_URL}/global/config", headers=H, json={
"provider": {"bailian-payg": {"options": {
"apiKey": api_key,
"baseURL": "https://dashscope.aliyuncs.com/apps/anthropic/v1",
}}}
})
# Create a session (specify the model)
session = requests.post(f"{BASE_URL}/session", headers=H, json={
"model": {"id": "qwen3.7-max", "providerID": "bailian-payg"},
}).json()
session_id = session["id"]
print(f"Session: {session_id}")
# Send a coding task (synchronously wait for the complete response)
resp = requests.post(f"{BASE_URL}/session/{session_id}/message", headers=H, json={
"parts": [{"type": "text", "text": "Write a Fibonacci program in Python, save it to /workspace/fibonacci.py and run it to verify"}]
}, timeout=300).json()
# Print the assistant response
for part in resp.get("parts", []):
if part["type"] == "text":
print(part["text"])
elif part["type"] == "tool":
print(f" [tool: {part.get('name')}]")
# Verify the generated file
print("\n--- fibonacci.py ---")
print(sandbox.commands.run("cat /workspace/fibonacci.py").stdout)
print("--- run ---")
print(sandbox.commands.run("python3 /workspace/fibonacci.py", timeout=30).stdout)
openclaw
The openclaw public template includes the openclaw service. Use the following code to get the access URL for the openclaw service in the sandbox.
from e2b import Sandbox
import os
import time, httpx
from urllib.parse import urlparse, parse_qs
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
OPENCLAW_GATEWAY_PORT = 18789
def get_gateway_token(sandbox_id, api_key, domain):
"""Get gateway authentication token via API"""
url = f"https://api.{domain}/sandboxes/{sandbox_id}"
headers = {
"X-API-KEY": api_key,
"X-Generate-Dashboard-Url": "true",
}
resp = httpx.get(url, headers=headers, timeout=10)
if resp.status_code != 200:
return None
data = resp.json()
dashboard_url = None
if "metadata" in data and isinstance(data["metadata"], dict):
meta = data["metadata"]
dashboard_url = meta.get("sandbox.alicloud.com/dashboard-url") or meta.get("dashboard_url")
if not dashboard_url:
dashboard_url = data.get("dashboard_url")
if dashboard_url:
parsed = urlparse(dashboard_url)
qs = parse_qs(parsed.query)
return qs.get("token", [None])[0]
return None
sandbox = Sandbox.create(
template="openclaw",
timeout=300000,
)
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
gateway_token = get_gateway_token(
sandbox.sandbox_id, os.environ["E2B_API_KEY"], os.environ["E2B_DOMAIN"]
)
url = f"https://{sandbox.get_host(OPENCLAW_GATEWAY_PORT)}?token={gateway_token}"
print(f" Sandbox Access URL: {url}")
qwenpaw
The qwenpaw public template includes the qwenpaw service, which starts automatically. Use the following code to get the access URL for the qwenpaw service in the sandbox.
import os
import time
from urllib.parse import urlparse, parse_qs
import httpx
from e2b import Sandbox
os.environ["E2B_DOMAIN"] = "<YOUR-DOMAIN>"
os.environ["E2B_API_KEY"] = "<YOUR-API-KEY>"
QWENPAW_PORT = 8088
def get_gateway_token(sandbox_id, api_key, domain):
"""Get the gateway authentication token via the API"""
url = f"https://api.{domain}/sandboxes/{sandbox_id}"
headers = {
"X-API-KEY": api_key,
"X-Generate-Dashboard-Url": "true",
}
resp = httpx.get(url, headers=headers, timeout=10)
if resp.status_code != 200:
return None
data = resp.json()
dashboard_url = None
if "metadata" in data and isinstance(data["metadata"], dict):
meta = data["metadata"]
dashboard_url = meta.get("sandbox.alicloud.com/dashboard-url") or meta.get("dashboard_url")
if not dashboard_url:
dashboard_url = data.get("dashboard_url")
if dashboard_url:
parsed = urlparse(dashboard_url)
qs = parse_qs(parsed.query)
return qs.get("token", [None])[0]
return None
sandbox = Sandbox.create(
template="qwenpaw",
timeout=300000,
)
# Wait for the sandbox to be ready
print(f"Waiting for sandbox {sandbox.sandbox_id} to be ready...")
for _ in range(30):
try:
if sandbox.is_running():
print("Sandbox is ready.")
break
except Exception:
pass
time.sleep(2)
gateway_token = get_gateway_token(
sandbox.sandbox_id, os.environ["E2B_API_KEY"], os.environ["E2B_DOMAIN"]
)
url = f"https://{sandbox.get_host(QWENPAW_PORT)}?token={gateway_token}"
print(f"Sandbox Access URL: {url}")