Skip to main content
The OpenClaw gateway serves a web UI for chatting with agents. When exposed over a network it requires token auth and device pairing — your browser must be approved as a trusted device before it can connect. This guide starts the gateway inside an E2B sandbox, prints the URL, and programmatically approves your browser.

Quick start

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. Start the gateway with token auth
sandbox.commands.run(
  `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}`)
// Open the URL in your browser before running the next step.
Open the URL in your browser. It will show “Pairing required” — that’s expected. Run the next step to approve your device.

Approve device pairing

The gateway requires each browser to be approved as a paired device. Run this after opening the URL — it polls for pending requests and approves the first one.
// Make sure you've opened the gateway URL in your browser before running this.

// 4. 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))
}
Once approved, the browser connects and the gateway UI loads.

How it works

StepWhat happens
--bind lanGateway listens on 0.0.0.0 so E2B can proxy it
--auth tokenRequires ?token= on the URL for HTTP and WebSocket auth
Browser opens URLGateway serves the UI, browser opens a WebSocket
code=1008 pairing requiredGateway closes the WebSocket until the device is approved
devices approveApproves the browser’s device fingerprint
Browser reconnectsWebSocket connects successfully, UI is live

Gateway flags reference

FlagPurpose
--allow-unconfiguredStart without a full config file
--bind lanBind to 0.0.0.0 (required for E2B port proxying)
--auth tokenEnable token-based authentication
--token <value>The auth token (passed as ?token= in the URL)
--port <number>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.
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))
}