OpenClaw is an open-source agent framework that supports CLI-based programmatic invocation and a Gateway Web UI. This article focuses on real-world task scenarios: deploying Gateway, Agent CLI, custom-domain browser access, security mode and operations, and Skill extension. For image addresses, building a Template, and environment and capability notes, see OpenClaw Template.
Prerequisites
-
Completed SDK integration (E2B API Key,
api_url, anddomain) -
Built a Template in the
readystate as described in OpenClaw Template, and recorded the template name (referred to asTEMPLATE_NAMEbelow) and theOPTSin the build example -
Prepared a model API Key, which is injected through
envswhen you create a sandbox. The image does not include pre-configured keys-
International: Obtain an API Key (starting with
sk-) from the Model Studio console
-
Deploy Gateway
Start the OpenClaw Gateway and interact with the Agent in a browser. The complete workflow is: Create a Sandbox → Configure → Start the Gateway → Get the access URL. For server-side programmatic invocation, see Agent CLI.
import time
from e2b_code_interpreter import Sandbox
# Gateway authentication token: set any string you want. You do not need to obtain it from an API.
# Pass it to openclaw gateway --token on startup, and pass it as the ?token= URL parameter for browser access.
# The official OpenClaw documentation also supports the OPENCLAW_APP_TOKEN environment variable, which works the same way.
TOKEN = "my-gateway-token"
PORT = 18789
BAILIAN_API_KEY = "<YOUR-BAILIAN-API-KEY>"
BAILIAN_BASE_URL = "[workspace-id].[region].maas.aliyuncs.com"
BAILIAN_MODEL = "qwen3.8-max"
# 1. Create a Sandbox
sandbox = Sandbox.create(
template=TEMPLATE_NAME,
timeout=3600,
envs={
"ANTHROPIC_AUTH_TOKEN": BAILIAN_API_KEY,
"ANTHROPIC_BASE_URL": BAILIAN_BASE_URL,
"ANTHROPIC_MODEL": BAILIAN_MODEL,
},
**OPTS,
)
# Register the Model Studio provider
sandbox.commands.run(
"openclaw onboard --non-interactive --accept-risk --skip-health "
"--auth-choice custom-api-key "
f"--custom-api-key {BAILIAN_API_KEY} "
f"--custom-base-url {BAILIAN_BASE_URL} "
"--custom-compatibility anthropic "
f"--custom-model-id {BAILIAN_MODEL} "
"--custom-provider-id bailian",
timeout=120,
)
# 2. Set the default model
sandbox.commands.run(
f"openclaw config set agents.defaults.model.primary bailian/{BAILIAN_MODEL}"
)
# 3. Configure the Control UI (required for public network access to the sandbox)
origin = f"https://{sandbox.get_host(PORT)}"
sandbox.commands.run(
f"openclaw config set gateway.controlUi.allowedOrigins '[\"{origin}\"]'"
)
# 4. Start the Gateway (in the background)
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 "
f"--token {TOKEN} --port {PORT}'",
background=True,
)
# 5. Wait until the Gateway is ready
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}")Agent CLI
Invoke the Agent programmatically on the server side without starting the Gateway.
If you have already created a Sandbox as described in Deploy Gateway, run the following commands on the same sandbox. You do not need to create another one.
If you only need the CLI without starting the Gateway:
from e2b_code_interpreter import Sandbox
BAILIAN_API_KEY = "<YOUR-BAILIAN-API-KEY>"
BAILIAN_BASE_URL = "[workspace-id].[region].maas.aliyuncs.com"
BAILIAN_MODEL = "qwen3.8-max"
sandbox = Sandbox.create(
template=TEMPLATE_NAME,
timeout=3600,
envs={
"ANTHROPIC_AUTH_TOKEN": BAILIAN_API_KEY,
"ANTHROPIC_BASE_URL": BAILIAN_BASE_URL,
"ANTHROPIC_MODEL": BAILIAN_MODEL,
},
**OPTS,
)
# Register the Model Studio provider
sandbox.commands.run(
"openclaw onboard --non-interactive --accept-risk --skip-health "
"--auth-choice custom-api-key "
f"--custom-api-key {BAILIAN_API_KEY} "
f"--custom-base-url {BAILIAN_BASE_URL} "
"--custom-compatibility anthropic "
f"--custom-model-id {BAILIAN_MODEL} "
"--custom-provider-id bailian",
timeout=120,
)
result = sandbox.commands.run(
f'openclaw agent --local --agent main --model bailian/{BAILIAN_MODEL} '
'-m "What is 2+2? Reply with just the number."',
timeout=120,
)
print(result.stdout)Browser access (FC Agent Sandbox)
To open the Gateway Control UI of FC Agent Sandbox in a browser, you must bind a custom domain first. The default platform domain (*.sandbox.aliyuncs.com) triggers a download instead of opening the page. After you bind a custom domain, the page loads normally. For the complete steps on adding a domain in the console, DNS resolution, HTTPS certificates, and SDK configuration, see Cloud Sandbox Custom Domains.
Configure a custom domain
Step 1: Configure the domain in the console. In the Function Compute console, bind a custom domain for FC Agent Sandbox, and configure the certificate and DNS records (api.<YOUR-CUSTOM-DOMAIN> / *.<YOUR-CUSTOM-DOMAIN>).
Step 2: Update OPTS in the SDK. The default OPTS in OpenClaw Template:
OPTS = {
"api_key": "<YOUR-E2B-API-KEY>",
"api_url": "https://api.cn-beijing.sandbox.aliyuncs.com",
"domain": "cn-beijing.sandbox.aliyuncs.com",
}
Replace the values with the custom domain that matches the console configuration (the api_key must belong to the same account that bound the custom domain):
OPTS = {
"api_key": "<YOUR-E2B-API-KEY>",
"api_url": "https://api.<YOUR-CUSTOM-DOMAIN>",
"domain": "<YOUR-CUSTOM-DOMAIN>",
}
Step 3: Pass OPTS throughout the workflow. Template.build(..., **OPTS), Sandbox.create(..., **OPTS), and all other SDK calls in this topic use the preceding OPTS. No additional parameters are needed.
After the configuration takes effect, sandbox.get_host(PORT) returns {PORT}-sbx-{sandbox_id}.<YOUR-CUSTOM-DOMAIN>. Use this host for the Gateway allowedOrigins, the browser address bar, and the <host> in the curl command below.
Open the Control UI
After you configure the custom domain and deploy the Gateway, public network access also requires authentication:
|
Authentication |
Source |
Purpose |
How to Pass |
|
Gateway Token |
The |
OpenClaw Control UI |
|
-
Run the preceding script and record the
GatewayURL in the output -
Open the Gateway URL (the URL already contains
?token=)
Verify the result with curl first:
curl -sI "https://<host>/?token=<Gateway Token>"
The expected response contains 200. If the Content-Type response header is text/html, the Gateway works as expected.
If "browser origin not allowed" is displayed, make sure that allowedOrigins exactly matches the origin in the address bar (https://{sandbox.get_host(PORT)}, without a trailing /), and restart the Gateway after the change.
Security mode
During testing, you can disable device pairing (the preceding example already sets dangerouslyDisableDeviceAuth true). If security mode is enabled, approve the pending device after you open the URL:
import json
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} "
f"--url ws://127.0.0.1:{PORT}"
)
print(f"Device approved: {rid}")
break
except Exception:
pass
time.sleep(2)
Restart Gateway
After you change the model or configuration, run the following in the current Sandbox:
origin = f"https://{sandbox.get_host(PORT)}"
sandbox.commands.run(
f"openclaw config set gateway.controlUi.allowedOrigins '[\"{origin}\"]'"
)
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" 2>/dev/null || true; done
done'"""
)
time.sleep(1)
sandbox.commands.run(
f"openclaw gateway --allow-unconfigured --bind lan --auth token "
f"--token {TOKEN} --port {PORT}",
background=True,
)
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)
Disable insecure settings
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" 2>/dev/null || true; done
done'"""
)
sandbox.commands.run(
f"openclaw gateway --allow-unconfigured --bind lan --auth token "
f"--token {TOKEN} --port {PORT}",
background=True,
)
Extend capabilities with Skills
OpenClaw extends Agent capabilities through Skills (a directory plus SKILL.md). Both the Gateway and the Agent CLI can use Skills. For more information, see OpenClaw Skills.
The following examples assume that you have created a sandbox as described above (by using the Gateway or the Agent CLI). The image does not preload custom Skills. You can run openclaw skills list to view the loaded Skills (including bundled ones). For path conventions, see OpenClaw Template.
The following example uses Model Studio (BAILIAN_MODEL is defined above).
Write a Managed Skill:
sandbox.files.write(
"/home/user/.openclaw/skills/summarize-changes/SKILL.md",
"""---
name: summarize-changes
description: Summarizes uncommitted changes and flags risks. Use when reviewing diffs or writing commit messages.
user-invocable: true
---
## Instructions
1. Run `git diff HEAD` and summarize changes in 2–3 bullets.
2. List risks such as missing error handling or hardcoded values.
3. If the diff is empty, say there are no uncommitted changes.
""",
)
# Verify that the skill is in the list
print(sandbox.commands.run("openclaw skills list").stdout)
result = sandbox.commands.run(
f'openclaw agent --local --agent main --model bailian/{BAILIAN_MODEL} '
'--message "/summarize-changes"',
timeout=0,
)
print(result.stdout)
Write a Workspace Skill:
sandbox.files.write(
"/home/user/.openclaw/workspace/skills/api-conventions/SKILL.md",
"""---
name: api-conventions
description: API design conventions. Use when adding or changing HTTP endpoints or /healthz.
user-invocable: true
---
When writing API endpoints:
- Use RESTful naming
- Return consistent error formats
- Include request validation
""",
)
result = sandbox.commands.run(
f'openclaw agent --local --agent main --model bailian/{BAILIAN_MODEL} '
'--message "/api-conventions Add a /healthz endpoint"',
timeout=0,
)
print(result.stdout)
Upload a multi-file Skill directory from your local machine:
If a Skill contains additional files such as scripts/ and references/, use files.write_files to write them in a single batch:
from pathlib import Path
def upload_skill_dir(sandbox, local_dir: str, remote_root: str) -> None:
local = Path(local_dir).resolve()
entries = []
for path in local.rglob("*"):
if path.is_file():
rel = path.relative_to(local).as_posix()
entries.append({
"path": f"{remote_root.rstrip('/')}/{rel}",
"data": path.read_bytes(),
})
sandbox.files.write_files(entries)
# Example local directory structure:
# ./my-skills/echo-marker/SKILL.md
# ./my-skills/echo-marker/scripts/marker.txt
upload_skill_dir(
sandbox,
"./my-skills/echo-marker",
"/home/user/.openclaw/skills/echo-marker", # Managed. For a Workspace Skill, use workspace/skills/...
)
print(sandbox.commands.run("openclaw skills list").stdout)
result = sandbox.commands.run(
f'openclaw agent --local --agent main --model bailian/{BAILIAN_MODEL} '
'--message "/echo-marker"',
timeout=0,
)
print(result.stdout)
About Template.copy: presetting files with.copy("local-dir", "/home/user/.openclaw/skills/...")duringTemplate.buildis not supported. To add custom Skills, use the runtimefiles.write/files.write_filesmethods described above.
Install from ClawHub (optional):
Sandboxes have outbound Internet access by default, so you can search for and install community Skills directly (browse clawhub.ai):
# Search
result = sandbox.commands.run("openclaw skills search calendar", timeout=120)
print(result.stdout)
# Install
sandbox.commands.run("openclaw skills install ws-calendar", timeout=180)
Parameters
|
Step |
Description |
|
|
The Gateway listens on |
|
|
Authenticate with the |
|
Open the URL in a browser |
The Gateway provides the UI, and the browser establishes a WebSocket connection |
|
|
In security mode, approve the device first |
|
|
Approve the browser device fingerprint |
|
Reconnect the browser |
The WebSocket connection succeeds, and the UI is available |
Cleanup
Release resources when the task is complete:
sandbox.kill()
Billing
A Sandbox is billed based on its CPU and memory specifications and running duration. Fees for model API calls are billed separately by the model service. For more information, see Billing Overview.
FAQ
|
Issue |
Solution |
|
The agent does not respond |
Verify that |
|
The Gateway cannot be opened in a browser |
Verify that the Gateway URL contains |
|
The browser downloads a file instead of opening the page |
Bind a custom domain. For more information, see Cloud Sandbox Custom Domains and Browser access (FC Agent Sandbox) |
|
|
The API Key and Cloud Sandbox Custom Domains must belong to the same account |
|
Origin not allowed |
|
|
Template build fails |
Verify that |
|
Other SDK or build issues |
References
-
OpenClaw Template (image addresses, building a Template, Gateway parameters, environment overview, and Skill capability notes)