diff --git a/docs.json b/docs.json
index da53d455..e39fae0c 100644
--- a/docs.json
+++ b/docs.json
@@ -56,6 +56,14 @@
"docs/agents/amp",
"docs/agents/claude-code",
"docs/agents/codex",
+ {
+ "group": "OpenClaw",
+ "icon": "/images/icons/openclaw.svg",
+ "pages": [
+ "docs/agents/openclaw/openclaw-gateway",
+ "docs/agents/openclaw/openclaw-telegram"
+ ]
+ },
"docs/agents/opencode"
]
},
diff --git a/docs/agents/openclaw/openclaw-gateway.mdx b/docs/agents/openclaw/openclaw-gateway.mdx
new file mode 100644
index 00000000..f6a1729d
--- /dev/null
+++ b/docs/agents/openclaw/openclaw-gateway.mdx
@@ -0,0 +1,277 @@
+---
+title: "Deploy OpenClaw"
+description: "Start the OpenClaw gateway in an E2B sandbox and connect your browser."
+icon: "globe"
+---
+
+## Quick start
+
+This launches your OpenClaw [gateway](https://docs.openclaw.ai/gateway) site (web UI for chatting with agents).
+
+
+```typescript JavaScript & TypeScript
+import { Sandbox } from 'e2b'
+
+const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
+const PORT = 18789
+
+// 1. Create sandbox
+const sandbox = await Sandbox.create('openclaw', {
+ envs: { OPENAI_API_KEY: process.env.OPENAI_API_KEY },
+ timeoutMs: 3600_000,
+})
+
+// 2. Set the default model
+await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2')
+
+// 3. Set insecure control UI flags and start the gateway with token auth
+await sandbox.commands.run(
+ `bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && ` +
+ `openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && ` +
+ `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}'`,
+ { background: true }
+)
+
+// 4. Wait for the gateway to start listening
+for (let i = 0; i < 45; i++) {
+ const probe = await sandbox.commands.run(
+ `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
+ )
+ if (probe.stdout.trim() === 'ready') break
+ await new Promise((r) => setTimeout(r, 1000))
+}
+
+const url = `https://${sandbox.getHost(PORT)}/?token=${TOKEN}`
+console.log(`Gateway: ${url}`)
+```
+```python Python
+import os, time
+from e2b import Sandbox
+
+TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
+PORT = 18789
+
+# 1. Create sandbox
+sandbox = Sandbox.create("openclaw", envs={
+ "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
+}, timeout=3600)
+
+# 2. Set the default model
+sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2")
+
+# 3. Set insecure control UI flags and start the gateway with token auth
+sandbox.commands.run(
+ f"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && "
+ f"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && "
+ f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}'",
+ background=True,
+)
+
+# 4. Wait for the gateway to start listening
+for _ in range(45):
+ probe = sandbox.commands.run(
+ f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
+ )
+ if probe.stdout.strip() == "ready":
+ break
+ time.sleep(1)
+
+url = f"https://{sandbox.get_host(PORT)}/?token={TOKEN}"
+print(f"Gateway: {url}")
+```
+
+
+Visit the printed `Gateway` URL in your browser.
+
+If you run in secure mode (set `gateway.controlUi.dangerouslyDisableDeviceAuth false`), run this after opening the URL to poll pending pairing requests and approve the first one.
+
+
+```typescript JavaScript & TypeScript
+// 5. Poll for the browser's pending device request and approve it
+for (let i = 0; i < 30; i++) {
+ try {
+ const res = await sandbox.commands.run(
+ `openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}`
+ )
+ const data = JSON.parse(res.stdout)
+ if (data.pending?.length) {
+ const rid = data.pending[0].requestId
+ await sandbox.commands.run(
+ `openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}`
+ )
+ console.log(`Device approved: ${rid}`)
+ break
+ }
+ } catch {}
+ await new Promise((r) => setTimeout(r, 2000))
+}
+```
+```python Python
+import json
+
+# 5. Poll for the browser's pending device request and approve it
+for _ in range(30):
+ try:
+ res = sandbox.commands.run(
+ f"openclaw devices list --json --url ws://127.0.0.1:{PORT} --token {TOKEN}"
+ )
+ data = json.loads(res.stdout)
+ if data.get("pending"):
+ rid = data["pending"][0]["requestId"]
+ sandbox.commands.run(
+ f"openclaw devices approve {rid} --token {TOKEN} --url ws://127.0.0.1:{PORT}"
+ )
+ print(f"Device approved: {rid}")
+ break
+ except Exception:
+ pass
+ time.sleep(2)
+```
+
+
+Once approved, the browser connects and the gateway UI loads.
+
+## How it works
+
+| Step | What happens |
+|------|-------------|
+| `--bind lan` | Gateway listens on `0.0.0.0` so E2B can proxy it |
+| `--auth token` | Requires `?token=` on the URL for HTTP and WebSocket auth |
+| Browser opens URL | Gateway serves the UI, browser opens a WebSocket |
+| `code=1008 pairing required` | Gateway closes the WebSocket until the device is approved (secure mode only) |
+| `devices approve` | Approves the browser's device fingerprint (secure mode only) |
+| Browser reconnects | WebSocket connects successfully, UI is live |
+
+## Gateway flags reference
+
+| Flag | Purpose |
+|------|---------|
+| `--allow-unconfigured` | Start without a full config file |
+| `--bind lan` | Bind to `0.0.0.0` (required for E2B port proxying) |
+| `--auth token` | Enable token-based authentication |
+| `--token ` | The auth token (passed as `?token=` in the URL) |
+| `--port ` | Gateway listen port (default: `18789`) |
+
+## How to restart the gateway
+
+Use this when the gateway is already running and you want a clean restart (for example, after changing model or env settings).
+
+
+We can't use the `openclaw gateway restart` command here. Some SDK environments cannot target a specific Unix user in `commands.run`. The commands below use the default command user context.
+
+
+
+```typescript JavaScript & TypeScript
+const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
+const PORT = 18789
+
+// 1) Kill existing gateway processes if present
+await sandbox.commands.run(
+ `bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
+)
+await new Promise((r) => setTimeout(r, 1000))
+
+// 2) Start gateway again
+await sandbox.commands.run(
+ `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
+ { background: true }
+)
+
+// 3) Wait for listening socket
+for (let i = 0; i < 45; i++) {
+ const probe = await sandbox.commands.run(
+ `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'`
+ )
+ if (probe.stdout.trim() === 'ready') break
+ await new Promise((r) => setTimeout(r, 1000))
+}
+```
+```python Python
+import os, time
+
+TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
+PORT = 18789
+
+# 1) Kill existing gateway processes if present
+sandbox.commands.run(
+ """bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
+for pid in $(pgrep -f "$p" || true); do
+ kill "$pid" >/dev/null 2>&1 || true
+done
+done'"""
+)
+time.sleep(1)
+
+# 2) Start gateway again
+sandbox.commands.run(
+ f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
+ background=True,
+)
+
+# 3) Wait for listening socket
+for _ in range(45):
+ probe = sandbox.commands.run(
+ f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\''
+ )
+ if probe.stdout.strip() == "ready":
+ break
+ time.sleep(1)
+```
+
+
+## Turn insecure flags off (recommended after testing)
+
+Use this to restore secure device authentication after initial testing.
+
+
+```typescript JavaScript & TypeScript
+const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token'
+const PORT = 18789
+
+await sandbox.commands.run(
+ `bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && ` +
+ `openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'`
+)
+
+await sandbox.commands.run(
+ `bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'`
+)
+
+await sandbox.commands.run(
+ `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`,
+ { background: true }
+)
+```
+```python Python
+import os
+
+TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token")
+PORT = 18789
+
+sandbox.commands.run(
+ "bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth false && "
+ "openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth false'"
+)
+
+sandbox.commands.run(
+ """bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do
+for pid in $(pgrep -f "$p" || true); do
+ kill "$pid" >/dev/null 2>&1 || true
+done
+done'"""
+)
+
+sandbox.commands.run(
+ f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}",
+ background=True,
+)
+```
+
+
+## Related
+
+
+
+ Connect OpenClaw to Telegram and approve pairing
+
+
diff --git a/docs/agents/openclaw/openclaw-telegram.mdx b/docs/agents/openclaw/openclaw-telegram.mdx
new file mode 100644
index 00000000..da2b0059
--- /dev/null
+++ b/docs/agents/openclaw/openclaw-telegram.mdx
@@ -0,0 +1,196 @@
+---
+title: "OpenClaw Telegram"
+description: "Connect OpenClaw to Telegram in an E2B sandbox, approve pairing, and chat through your bot."
+icon: "paper-plane"
+---
+
+OpenClaw supports Telegram as a chat channel. In E2B you can run OpenClaw in a sandbox, attach your bot token, and approve user pairing from the terminal.
+
+This guide covers the working flow we used:
+
+1. Start OpenClaw in a sandbox.
+2. Enable the Telegram plugin.
+3. Add Telegram channel credentials.
+4. Start the channel runtime in background.
+5. Approve Telegram pairing.
+
+## Prerequisites
+
+- A Telegram bot token from [@BotFather](https://t.me/BotFather). There's instructions to follow there, it runs /newbot for you and walks you through naming and creating your bot.
+- An OpenAI API key for the OpenClaw model.
+- E2B API key configured locally.
+
+## Quick start
+
+
+```typescript JavaScript & TypeScript
+import { Sandbox } from 'e2b'
+
+const GATEWAY_PORT = 18789
+const GATEWAY_TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token'
+
+const sandbox = await Sandbox.create('openclaw', {
+ envs: {
+ OPENAI_API_KEY: process.env.OPENAI_API_KEY,
+ TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN,
+ },
+ timeoutMs: 3600_000,
+})
+
+await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2')
+
+// Enable the Telegram plugin (required before adding the channel)
+await sandbox.commands.run('openclaw config set plugins.entries.telegram.enabled true')
+await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"')
+
+await sandbox.commands.run(
+ `bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && ` +
+ `openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && ` +
+ `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${GATEWAY_TOKEN} --port ${GATEWAY_PORT}'`,
+ { background: true }
+)
+
+for (let i = 0; i < 45; i++) {
+ const probe = await sandbox.commands.run(
+ `bash -lc 'ss -ltn | grep -q ":${GATEWAY_PORT} " && echo ready || echo waiting'`
+ )
+ if (probe.stdout.trim() === 'ready') break
+ await new Promise((r) => setTimeout(r, 1000))
+}
+```
+```python Python
+import os
+import time
+from e2b import Sandbox
+
+GATEWAY_PORT = 18789
+GATEWAY_TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token")
+
+sandbox = Sandbox.create("openclaw", envs={
+ "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"],
+ "TELEGRAM_BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"],
+}, timeout=3600)
+
+sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2")
+
+# Enable the Telegram plugin (required before adding the channel)
+sandbox.commands.run("openclaw config set plugins.entries.telegram.enabled true")
+sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"')
+
+sandbox.commands.run(
+ f"bash -lc 'openclaw config set gateway.controlUi.allowInsecureAuth true && "
+ f"openclaw config set gateway.controlUi.dangerouslyDisableDeviceAuth true && "
+ f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {GATEWAY_TOKEN} --port {GATEWAY_PORT}'",
+ background=True,
+)
+
+for _ in range(45):
+ probe = sandbox.commands.run(
+ f"bash -lc 'ss -ltn | grep -q \":{GATEWAY_PORT} \" && echo ready || echo waiting'"
+ )
+ if probe.stdout.strip() == "ready":
+ break
+ time.sleep(1)
+```
+
+
+
+For Telegram setup, you do **not** need to open the gateway URL in a browser. The gateway process is used here as a long-running channel runtime.
+
+
+## Pair your Telegram user
+
+1. Open your bot in Telegram and send a message (for example: `hi`).
+2. Telegram will return a pairing prompt similar to:
+
+```text
+OpenClaw: access not configured.
+
+Your Telegram user id: ...
+Pairing code: XXXXXXXX
+
+Ask the bot owner to approve with:
+openclaw pairing approve telegram XXXXXXXX
+```
+
+3. Approve that pairing code via `sandbox.commands.run(...)`:
+
+
+```typescript JavaScript & TypeScript
+const PAIRING_CODE = 'XXXXXXXX' // from Telegram
+
+await sandbox.commands.run(
+ `openclaw pairing approve --channel telegram ${PAIRING_CODE}`
+)
+```
+```python Python
+PAIRING_CODE = "XXXXXXXX" # from Telegram
+
+sandbox.commands.run(
+ f"openclaw pairing approve --channel telegram {PAIRING_CODE}"
+)
+```
+
+
+`openclaw pairing approve telegram ` also works if you prefer that form.
+
+## Verify channel status
+
+
+```typescript JavaScript & TypeScript
+const channels = await sandbox.commands.run('openclaw channels list --json')
+const status = await sandbox.commands.run('openclaw channels status --json --probe')
+const pairing = await sandbox.commands.run('openclaw pairing list --json --channel telegram')
+
+console.log(JSON.parse(channels.stdout))
+console.log(JSON.parse(status.stdout))
+console.log(JSON.parse(pairing.stdout))
+```
+```python Python
+import json
+
+channels = sandbox.commands.run("openclaw channels list --json")
+status = sandbox.commands.run("openclaw channels status --json --probe")
+pairing = sandbox.commands.run("openclaw pairing list --json --channel telegram")
+
+print(json.loads(channels.stdout))
+print(json.loads(status.stdout))
+print(json.loads(pairing.stdout))
+```
+
+
+If you need logs from channel handlers:
+
+
+```typescript JavaScript & TypeScript
+const logs = await sandbox.commands.run(
+ 'openclaw channels logs --channel telegram --lines 200'
+)
+console.log(logs.stdout)
+```
+```python Python
+logs = sandbox.commands.run(
+ "openclaw channels logs --channel telegram --lines 200"
+)
+print(logs.stdout)
+```
+
+
+## Troubleshooting
+
+- `Unknown channel: telegram`
+ - The Telegram plugin is not enabled. Run `openclaw config set plugins.entries.telegram.enabled true` before adding the channel.
+- `OpenClaw: access not configured`
+ - Pairing has not been approved yet. Run `openclaw pairing approve ...`.
+- `No API key found for provider ...`
+ - This guide uses `openai/gpt-5.2`. Set `OPENAI_API_KEY` in sandbox envs.
+- No pending pairing requests from `pairing list`
+ - Send a fresh message to the bot first, then retry `pairing list --channel telegram`.
+
+## Related
+
+
+
+ Run OpenClaw's web gateway with token auth
+
+