docs/tools/browser-control.md
For setup, configuration, and troubleshooting, see Browser.
This page is the reference for the local control HTTP API, the openclaw browser
CLI, and scripting patterns (snapshots, refs, waits, debug flows).
For local integrations only, the Gateway exposes a small loopback HTTP API:
GET /, POST /start, POST /stopGET /tabs, POST /tabs/open, POST /tabs/focus, DELETE /tabs/:targetIdGET /snapshot, POST /screenshotPOST /navigate, POST /actPOST /hooks/file-chooser, POST /hooks/dialogPOST /download, POST /wait/downloadPOST /permissions/grantGET /console, POST /pdfGET /errors, GET /requests, POST /trace/start, POST /trace/stop, POST /highlightPOST /response/bodyGET /cookies, POST /cookies/set, POST /cookies/clearGET /storage/:kind, POST /storage/:kind/set, POST /storage/:kind/clearPOST /set/offline, POST /set/headers, POST /set/credentials, POST /set/geolocation, POST /set/media, POST /set/timezone, POST /set/locale, POST /set/deviceAll endpoints accept ?profile=<name>. POST /start?headless=true requests a
one-shot headless launch for local managed profiles without changing persisted
browser config; attach-only, remote CDP, and existing-session profiles reject
that override because OpenClaw does not launch those browser processes.
If shared-secret gateway auth is configured, browser HTTP routes require auth too:
Authorization: Bearer <gateway token>x-openclaw-password: <gateway password> or HTTP Basic auth with that passwordNotes:
gateway.auth.mode is none or trusted-proxy, these loopback browser
routes do not inherit those identity-bearing modes; keep them loopback-only./act error contractPOST /act uses a structured error response for route-level validation and
policy failures:
{ "error": "<message>", "code": "ACT_*" }
Current code values:
ACT_KIND_REQUIRED (HTTP 400): kind is missing or unrecognized.ACT_INVALID_REQUEST (HTTP 400): action payload failed normalization or validation.ACT_SELECTOR_UNSUPPORTED (HTTP 400): selector was used with an unsupported action kind.ACT_EVALUATE_DISABLED (HTTP 403): evaluate (or wait --fn) is disabled by config.ACT_TARGET_ID_MISMATCH (HTTP 403): top-level or batched targetId conflicts with request target.ACT_EXISTING_SESSION_UNSUPPORTED (HTTP 501): action is not supported for existing-session profiles.Other runtime failures may still return { "error": "<message>" } without a
code field.
Some features (navigate/act/AI snapshot/role snapshot, element screenshots, PDF) require Playwright. If Playwright isn’t installed, those endpoints return a clear 501 error.
What still works without Playwright:
--interactive, --compact,
--depth, --efficient) when a per-tab CDP WebSocket is available. This is
a fallback for inspection and ref discovery; Playwright remains the primary
action engine.openclaw browser when a per-tab CDP
WebSocket is availableexisting-session / Chrome MCP profilesexisting-session ref-based screenshots (--ref) from snapshot outputWhat still needs Playwright:
navigateact--element)Element screenshots also reject --full-page; the route returns fullPage is not supported for element screenshots.
If you see Playwright is not available in this gateway build, the packaged
Gateway is missing the core browser runtime dependency. Reinstall or update
OpenClaw, then restart the gateway. For Docker, also install the Chromium
browser binaries as shown below.
If your Gateway runs in Docker, avoid npx playwright (npm override conflicts).
Use the bundled CLI instead:
docker compose run --rm openclaw-cli \
node /app/node_modules/playwright-core/cli.js install chromium
To persist browser downloads, set PLAYWRIGHT_BROWSERS_PATH (for example,
/home/node/.cache/ms-playwright) and make sure /home/node is persisted via
OPENCLAW_HOME_VOLUME or a bind mount. See Docker.
A small loopback control server accepts HTTP requests and connects to Chromium-based browsers via CDP. Advanced actions (click/type/snapshot/PDF) go through Playwright on top of CDP; when Playwright is missing, only non-Playwright operations are available. The agent sees one stable interface while local/remote browsers and profiles swap freely underneath.
All commands accept --browser-profile <name> to target a specific profile, and --json for machine-readable output.
openclaw browser status
openclaw browser start
openclaw browser start --headless # one-shot local managed headless launch
openclaw browser stop # also clears emulation on attach-only/remote CDP
openclaw browser tabs
openclaw browser tab # shortcut for current tab
openclaw browser tab new
openclaw browser tab select 2
openclaw browser tab close 2
openclaw browser open https://example.com
openclaw browser focus abcd1234
openclaw browser close abcd1234
openclaw browser screenshot
openclaw browser screenshot --full-page
openclaw browser screenshot --ref 12 # or --ref e12
openclaw browser screenshot --labels
openclaw browser snapshot
openclaw browser snapshot --format aria --limit 200
openclaw browser snapshot --interactive --compact --depth 6
openclaw browser snapshot --efficient
openclaw browser snapshot --labels
openclaw browser snapshot --urls
openclaw browser snapshot --selector "#main" --interactive
openclaw browser snapshot --frame "iframe#main" --interactive
openclaw browser console --level error
openclaw browser errors --clear
openclaw browser requests --filter api --clear
openclaw browser pdf
openclaw browser responsebody "**/api" --max-chars 5000
openclaw browser navigate https://example.com
openclaw browser resize 1280 720
openclaw browser click 12 --double # or e12 for role refs
openclaw browser click-coords 120 340 # viewport coordinates
openclaw browser type 23 "hello" --submit
openclaw browser press Enter
openclaw browser hover 44
openclaw browser scrollintoview e12
openclaw browser drag 10 11
openclaw browser select 9 OptionA OptionB
openclaw browser download e12 report.pdf
openclaw browser waitfordownload report.pdf
openclaw browser upload /tmp/openclaw/uploads/file.pdf
openclaw browser fill --fields '[{"ref":"1","type":"text","value":"Ada"}]'
openclaw browser dialog --accept
openclaw browser wait --text "Done"
openclaw browser wait "#main" --url "**/dash" --load networkidle --fn "window.ready===true"
openclaw browser evaluate --fn '(el) => el.textContent' --ref 7
openclaw browser highlight e12
openclaw browser trace start
openclaw browser trace stop
openclaw browser cookies
openclaw browser cookies set session abc123 --url "https://example.com"
openclaw browser cookies clear
openclaw browser storage local get
openclaw browser storage local set theme dark
openclaw browser storage session clear
openclaw browser set offline on
openclaw browser set headers --headers-json '{"X-Debug":"1"}'
openclaw browser set credentials user pass # --clear to remove
openclaw browser set geo 37.7749 -122.4194 --origin "https://example.com"
openclaw browser set media dark
openclaw browser set timezone America/New_York
openclaw browser set locale en-US
openclaw browser set device "iPhone 14"
Notes:
upload and dialog are arming calls; run them before the click/press that triggers the chooser/dialog.click/type/etc require a ref from snapshot (numeric 12, role ref e12, or actionable ARIA ref ax12). CSS selectors are intentionally not supported for actions. Use click-coords when the visible viewport position is the only reliable target./tmp/openclaw{,/downloads,/uploads} (fallback: ${os.tmpdir()}/openclaw/...).upload can also set file inputs directly via --input-ref or --element.Stable tab ids and labels survive Chromium raw-target replacement when OpenClaw
can prove the replacement tab, such as same URL or a single old tab becoming a
single new tab after form submission. Raw target ids are still volatile; prefer
suggestedTargetId from tabs in scripts.
Snapshot flags at a glance:
--format ai (default with Playwright): AI snapshot with numeric refs (aria-ref="<n>").--format aria: accessibility tree with axN refs. When Playwright is available, OpenClaw binds refs with backend DOM ids to the live page so follow-up actions can use them; otherwise treat the output as inspection-only.--efficient (or --mode efficient): compact role snapshot preset. Set browser.snapshotDefaults.mode: "efficient" to make this the default (see Gateway configuration).--interactive, --compact, --depth, --selector force a role snapshot with ref=e12 refs. --frame "<iframe>" scopes role snapshots to an iframe.--labels adds a viewport-only screenshot with overlayed ref labels (prints MEDIA:<path>).--urls appends discovered link destinations to AI snapshots.OpenClaw supports two “snapshot” styles:
AI snapshot (numeric refs): openclaw browser snapshot (default; --format ai)
openclaw browser click 12, openclaw browser type 23 "hello".aria-ref.Role snapshot (role refs like e12): openclaw browser snapshot --interactive (or --compact, --depth, --selector, --frame)
[ref=e12] (and optional [nth=1]).openclaw browser click e12, openclaw browser highlight e12.getByRole(...) (plus nth() for duplicates).--labels to include a viewport screenshot with overlayed e12 labels.--urls when link text is ambiguous and the agent needs concrete
navigation targets.ARIA snapshot (ARIA refs like ax12): openclaw browser snapshot --format aria
openclaw browser click ax12 works when the snapshot path can bind
the ref through Playwright and Chrome backend DOM ids.If Playwright is unavailable, ARIA snapshots can still be useful for
inspection, but refs may not be actionable. Re-snapshot with --format ai
or --interactive when you need action refs.
Docker proof for the raw-CDP fallback path: pnpm test:docker:browser-cdp-snapshot
starts Chromium with CDP, runs browser doctor --deep, and verifies role
snapshots include link URLs, cursor-promoted clickables, and iframe metadata.
Ref behavior:
snapshot and use a fresh ref./act returns the current raw targetId after action-triggered replacement
when it can prove the replacement tab. Keep using stable tab ids/labels for
follow-up commands.--frame, role refs are scoped to that iframe until the next role snapshot.axN refs fail fast instead of falling through to
Playwright's aria-ref selector. Run a fresh snapshot on the same tab when
that happens.You can wait on more than just time/text:
openclaw browser wait --url "**/dash"openclaw browser wait --load networkidleopenclaw browser wait --fn "window.ready===true"openclaw browser wait "#main"These can be combined:
openclaw browser wait "#main" \
--url "**/dash" \
--load networkidle \
--fn "window.ready===true" \
--timeout-ms 15000
When an action fails (e.g. “not visible”, “strict mode violation”, “covered”):
openclaw browser snapshot --interactiveclick <ref> / type <ref> (prefer role refs in interactive mode)openclaw browser highlight <ref> to see what Playwright is targetingopenclaw browser errors --clearopenclaw browser requests --filter api --clearopenclaw browser trace startopenclaw browser trace stop (prints TRACE:<path>)--json is for scripting and structured tooling.
Examples:
openclaw browser status --json
openclaw browser snapshot --interactive --json
openclaw browser requests --filter api --json
openclaw browser cookies --json
Role snapshots in JSON include refs plus a small stats block (lines/chars/refs/interactive) so tools can reason about payload size and density.
These are useful for “make the site behave like X” workflows:
cookies, cookies set, cookies clearstorage local|session get|set|clearset offline on|offset headers --headers-json '{"X-Debug":"1"}' (legacy set headers --json '{"X-Debug":"1"}' remains supported)set credentials user pass (or --clear)set geo <lat> <lon> --origin "https://example.com" (or --clear)set media dark|light|no-preference|noneset timezone ..., set locale ...set device "iPhone 14" (Playwright device presets)set viewport 1280 720browser act kind=evaluate / openclaw browser evaluate and wait --fn
execute arbitrary JavaScript in the page context. Prompt injection can steer
this. Disable it with browser.evaluateEnabled=false if you do not need it.Strict-mode example (block private/internal destinations by default):
{
browser: {
ssrfPolicy: {
dangerouslyAllowPrivateNetwork: false,
hostnameAllowlist: ["*.example.com", "example.com"],
allowedHostnames: ["localhost"], // optional exact allow
},
},
}