OpenClaw Browser Control API, CLI, and Scripting Guide
Reference for the OpenClaw browser control HTTP API, CLI, and scripting actions. Covers snapshots, refs, waits, and debug flows for local integrations and automation.
Read this when
- Scripting or debugging the agent browser via the local control API
- Looking for the `openclaw browser` CLI reference
- Adding custom browser automation with snapshots and refs
For setup, configuration, and troubleshooting guidance, refer to Browser.
This page documents the local control HTTP API, the openclaw browser CLI, and scripting patterns covering snapshots, refs, waits, and debug flows.
Control API (optional)
The Gateway provides a small loopback HTTP API intended for local integrations only. This standalone server is optional: set the OPENCLAW_EAGER_BROWSER_CONTROL_SERVER=1 environment variable in the gateway service environment and restart the gateway to make the HTTP endpoints active. Without that variable, the browser control runtime continues to function through the CLI and agent tools, but no process listens on the loopback control port.
- Status/start/stop:
GET /,GET /doctor,POST /start,POST /stop,POST /reset-profile - Profiles:
GET /profiles,POST /profiles/create,DELETE /profiles/:name - Tabs:
GET /tabs,POST /tabs/open,POST /tabs/focus,DELETE /tabs/:targetId,POST /tabs/action - Snapshot/screenshot:
GET /snapshot,POST /screenshot - Actions:
POST /navigate,POST /act - Hooks:
POST /hooks/file-chooser,POST /hooks/dialog - Downloads:
POST /download,POST /wait/download - Permissions:
POST /permissions/grant - Debugging:
GET /console,POST /pdf - Debugging:
GET /errors,GET /requests,GET /dialogs,POST /trace/start,POST /trace/stop,POST /highlight - Network:
POST /response/body - State:
GET /cookies,POST /cookies/set,POST /cookies/clear - State:
GET /storage/:kind,POST /storage/:kind/set,POST /storage/:kind/clear - Settings:
POST /set/offline,POST /set/headers,POST /set/credentials,POST /set/geolocation,POST /set/media,POST /set/timezone,POST /set/locale,POST /set/device
Internally, the CLI relies on POST /tabs/action, the batched form used for browser tab subcommands ({"action":"new"|"label"|"select"|"close"|"list", ...}); when scripting directly, favor the dedicated tab routes listed above.
Every endpoint accepts ?profile=<name>. For local managed profiles, POST /start?headless=true triggers a one-shot headless launch without altering persisted browser configuration; attach-only, remote CDP, and existing-session profiles reject that override, as OpenClaw does not start those browser processes.
For tab endpoints, targetId serves as the compatibility field name. Passing suggestedTargetId from GET /tabs or POST /tabs/open is preferred; labels and tabId handles such as t1 are also valid. Raw CDP target ids and unique raw target-id prefixes remain usable, though they are volatile diagnostic handles.
When shared-secret gateway auth is active, the browser HTTP endpoints also demand authentication:
Authorization: Bearer <gateway token>x-openclaw-password: <gateway password>, or HTTP Basic auth using that same password
Additional notes:
- This loopback-only browser API does not rely on trusted-proxy or Tailscale Serve identity headers.
- When
gateway.auth.modeis set tononeortrusted-proxy, these loopback browser routes do not adopt those identity-aware modes; they must remain loopback-only.
/act error contract
POST /act responds with a structured error payload for validation and policy
failures at the route level:
{ "error": "<message>", "code": "ACT_*" }
Current code options:
ACT_KIND_REQUIRED(HTTP 400):kindis absent or not recognized.ACT_INVALID_REQUEST(HTTP 400): the action payload could not be normalized or validated.ACT_SELECTOR_UNSUPPORTED(HTTP 400):selectorwas paired with an action kind that isn't supported.ACT_EVALUATE_DISABLED(HTTP 403):evaluate(orwait --fn) is turned off by configuration.ACT_TARGET_ID_MISMATCH(HTTP 403): top-level or batchedtargetIddoes not match the request target.ACT_EXISTING_SESSION_UNSUPPORTED(HTTP 501): the action is unsupported for profiles tied to existing sessions.
Other runtime errors may still surface as { "error": "<message>" } without a
code field.
Playwright requirement
Several capabilities (navigate/act/AI snapshot/role snapshot, element screenshots, PDF) depend on Playwright. Without Playwright installed, those routes respond with a clear 501 error.
Operations that remain functional without Playwright:
- ARIA snapshots
- Role-style accessibility snapshots (
--interactive,--compact,--depth,--efficient) when a per-tab CDP WebSocket is reachable. This serves as a fallback for inspection and ref discovery; Playwright stays the primary action engine. - Page screenshots for the managed
openclawbrowser when a per-tab CDP WebSocket is available - Page screenshots for
existing-session/ Chrome MCP profiles existing-sessionref-based screenshots (--ref) derived from snapshot output
Operations that still require Playwright:
navigateact- AI snapshots relying on Playwright's native AI snapshot format
- CSS-selector element screenshots (
--element) - full browser PDF export
Element screenshots also reject --full-page; the route returns fullPage is not supported for element screenshots.
Encountering Playwright is not available in this gateway build means the packaged
Gateway lacks the core browser runtime dependency. Reinstall or update
OpenClaw and restart the gateway. For Docker, also add the Chromium
browser binaries using the steps below.
Docker Playwright install
When your Gateway is Dockerized, steer clear of npx playwright (npm override conflicts).
For custom images, embed Chromium directly into the image:
OPENCLAW_INSTALL_BROWSER=1 ./scripts/docker/setup.sh
The browser depends on system libraries too, so installing Chromium in a temporary
Compose container won't persist. Rebuild the image with
OPENCLAW_INSTALL_BROWSER=1 instead. To keep browser downloads and other
caches, persist /home/node via OPENCLAW_HOME_VOLUME or a bind mount. Refer to
Docker.
How it works (internal)
A minimal loopback control server handles HTTP requests and links to Chromium-based browsers through CDP. Advanced operations (click/type/snapshot/PDF) use Playwright layered over CDP; when Playwright is absent, only non-Playwright actions are accessible. The agent sees a single stable interface while local/remote browsers and profiles change underneath without disruption.
CLI quick reference
Every command accepts --browser-profile <name> to select a specific profile, and --json for machine-readable output.
Basics: status, tabs, open/focus/close
openclaw browser status
openclaw browser doctor
openclaw browser doctor --deep # add a live snapshot probe
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 reset-profile # moves the profile's browser data to Trash
openclaw browser tabs
openclaw browser tab # shortcut for current tab
openclaw browser tab new
openclaw browser tab new --label research
openclaw browser tab label abcd1234 research
openclaw browser tab select 2
openclaw browser tab close 2
openclaw browser open https://example.com
openclaw browser focus abcd1234
openclaw browser close abcd1234
Profiles: list, create, delete
openclaw browser profiles
openclaw browser create-profile --name research --color "#0066CC"
openclaw browser create-profile --name attach --driver existing-session --cdp-url http://127.0.0.1:9222
openclaw browser delete-profile --name research
Inspection: screenshot, snapshot, console, errors, requests
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 snapshot --out snapshot.txt
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
Actions: navigate, click, type, drag, wait, evaluate
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 upload /tmp/openclaw/uploads/file.pdf --ref e12
openclaw browser upload media://inbound/file.pdf
openclaw browser fill --fields '[{"ref":"1","type":"text","value":"Ada"}]'
openclaw browser dialog --accept
openclaw browser dialog --dismiss --dialog-id d1
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 evaluate --fn 'const title = document.title; return title;'
openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }'
openclaw browser highlight e12
openclaw browser trace start
openclaw browser trace stop
State: cookies, storage, offline, headers, geo, device
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:
- The agent-facing
browsertool exposesaction=download(with requiredrefandpath) alongsideaction=waitfordownload(wherepathis optional). Both yield the stored download URL, a suggested filename, and a guarded local path. Explicit download interception applies to managed Playwright profiles; existing-session profiles respond with an unsupported-operation error. - Favor atomic chooser uploads: supply the trigger
--reftogether with the upload so OpenClaw arms and clicks within a single request. Paths-onlyuploadstays available when a later trigger is deliberate. Use--input-refor--elementto assign a file input directly.dialogserves as an arming call; invoke it before the click or press that opens the dialog. When an action brings up a modal, the action response carriesblockedByDialogandbrowserState.dialogs.pending; hand thatdialogIdback to respond directly. Dialogs managed outside OpenClaw show up underbrowserState.dialogs.recent. click/type/etc demand arefsourced fromsnapshot(numeric12, role refe12, or actionable ARIA refax12). CSS selectors are deliberately unsupported for actions. Turn toclick-coordswhen the visible viewport position is the sole dependable target.- Download and trace paths stay confined to OpenClaw temp roots:
/tmp/openclaw{,/downloads}(fallback:${os.tmpdir()}/openclaw/...). uploadaccepts files from the OpenClaw temp uploads root and OpenClaw-managed inbound media. Managed inbound media may be cited asmedia://inbound/<id>, sandbox-relativemedia/inbound/<id>, or a resolved path inside the managed inbound media directory. Nested media refs, traversal, symlinks, hardlinks, and arbitrary local paths remain rejected.uploadcan likewise set file inputs directly through--input-refor--element.
Stable tab ids and labels persist across Chromium raw-target replacement when
OpenClaw can demonstrate the replacement tab, for instance a unique old/new
pair for the same URL or a single old tab turning into a single new tab after
form submission. Ambiguous duplicate-URL replacements get fresh handles. Raw
target ids stay volatile; favor suggestedTargetId from tabs in scripts.
Snapshot flags summarized:
--format ai(default with Playwright): AI snapshot with numeric refs (aria-ref="<n>").--format aria: accessibility tree withaxNrefs. When Playwright is present, OpenClaw ties refs to backend DOM ids on the live page so follow-up actions can consume them; otherwise treat the output as inspection-only.--efficient(or--mode efficient): compact role snapshot preset. Setbrowser.snapshotDefaults.mode: "efficient"to make this the default (see Gateway configuration).--interactive,--compact,--depth,--selectorforce a role snapshot withref=e12refs.--frame "<iframe>"limits role snapshots to an iframe.- With Playwright,
--labelsadds a screenshot with overlaid ref labels (printsMEDIA:<path>) plus anannotationsarray containing each ref's bounding box. Onscreenshot, Playwright-backed labels work with--full-page,--ref, and--element; onsnapshot, the accompanying screenshot stays viewport-only. Existing-session/chrome-mcp profiles draw overlay labels on page screenshots but do not returnannotationsor employ the Playwright full-page/ref/element projection helper. Without Playwright or chrome-mcp, labeled screenshots are unavailable. --urlsappends discovered link destinations to AI snapshots.
Snapshots and refs
OpenClaw offers two "snapshot" varieties:
-
AI snapshot (numeric refs):
openclaw browser snapshot(default;--format ai)- Output: a text snapshot that includes numeric refs.
- Actions:
openclaw browser click 12,openclaw browser type 23 "hello". - Internally, the ref is resolved via Playwright's
aria-ref.
-
Role snapshot (role refs like
e12):openclaw browser snapshot --interactive(or--compact,--depth,--selector,--frame)- Output: a role-based list/tree with
[ref=e12](and optional[nth=1]). - Actions:
openclaw browser click e12,openclaw browser highlight e12. - Internally, the ref is resolved via
getByRole(...)(plusnth()for duplicates). - Add
--labelsto include a screenshot with overlayede12labels. On Playwright-backed profiles this also returns per-ref bounding-box metadata (annotations[]). - Add
--urlswhen link text is ambiguous and the agent needs concrete navigation targets.
- Output: a role-based list/tree with
-
ARIA snapshot (ARIA refs like
ax12):openclaw browser snapshot --format aria- Output: the accessibility tree as structured nodes.
- Actions:
openclaw browser click ax12works 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 aior--interactivewhen you need action refs. -
When the driver exposes stable document identity, consecutive AI and role snapshots for the same profile, tab, document, and option family append
[new]to ref-bearing lines absent from the previous snapshot. Navigation starts a fresh unmarked baseline; existing-session snapshots omit deltas. The first snapshot establishes the baseline without markers; later responses also exposenewElements, and add a count footer when the value is nonzero. Structured--format ariasnapshots withaxNrefs do not use delta markers. -
Docker proof for the raw-CDP fallback path:
pnpm test:docker:browser-cdp-snapshotstarts Chromium with CDP, runsbrowser doctor --deep, and verifies role snapshots include link URLs, cursor-promoted clickables, and iframe metadata.
Ref behavior:
- Refs are not stable across navigations; if something fails, re-run
snapshotand use a fresh ref. - A batch stops after a committed main-frame navigation, including a same-URL
reload, or after the page closes. Its
abortedsummary reports the action number and skipped count; take a fresh snapshot before issuing dependent actions, or use separate act calls when navigation is expected. /actreturns the current rawtargetIdafter action-triggered replacement when it can prove the replacement tab. Keep using stable tab ids/labels for follow-up commands.- If the role snapshot was taken with
--frame, role refs are scoped to that iframe until the next role snapshot. - Unknown or stale
axNrefs fail fast instead of falling through to Playwright'saria-refselector. Run a fresh snapshot on the same tab when that happens.
Browser batch CLI
openclaw browser batch executes a sequence of nested /act operations within a single /act invocation, reusing the same kind="batch" runtime that the agent tool accesses. This lets CLI users and scripts bundle actions such as wait, click, type, and evaluate into one replayable workflow, avoiding separate round trips for each step. Every element inside actions[] must be a BrowserActRequest, meaning the closed union accepted by the /act endpoint (click, clickCoords, type, press, hover, scrollIntoView, drag, select, fill, resize, wait, evaluate, close, batch), rather than free-form openclaw browser subcommands. On profile="user" and other chrome-mcp profiles tied to existing sessions, batch is unavailable; those cases require sending actions one at a time.
- CLI usage: pipe the JSON array through stdin using
openclaw browser batch --actions '<json>',openclaw browser batch --actions-file plan.json, oropenclaw browser batch --actions-file -. The--continueflag controlsstopOnError=false; by default execution halts at the first error. With--target-id, the entire batch is confined to a single tab. - Ref lifecycle: refs originate from a
snapshotcall made before the batch begins, since snapshotting is not treated as a nested action. When a nested action alters page state, for instance aclickthat causes navigation or anevaluatethat modifies the DOM, earlier refs may become stale for the remainder of the batch. Order state-changing actions early, or break them into a subsequent batch after taking a fresh snapshot. Navigation and re-snapshotting occur outside the batch viaopenclaw browser navigateandsnapshot, becauseopen,navigate, andsnapshotdo not count as/actaction types. - Target id conflicts: a nested action can leave out
targetIdor reuse the request-leveltargetId; if an explicit nestedtargetIdpoints to a different tab, the batch is rejected withACT_TARGET_ID_MISMATCHbefore any action executes. Batched actions deliberately share the request's tab. - Error summary: the response is
{ "results": [{ "ok": true }, { "ok": false, "error": "<message>" }, ...] }, listing one entry per action in sequence. With the defaultstopOnError, the list truncates at the first failure; setting--continuereturns results for every action. Any failed entry causes the CLI to exit with a nonzero code; adding--jsonkeeps the full ordered response available for scripts.
Wait power-ups
Beyond time and text, other conditions can be awaited:
- Wait for URL (Playwright glob patterns are supported):
openclaw browser wait --url "**/dash"
- Wait for load state:
openclaw browser wait --load networkidle- This works on managed
openclawand raw/remote CDP profiles. Profiles running theexisting-sessiondriver, including the defaultuserprofile, will rejectnetworkidle; instead, rely on--url,--text, a selector, or--fnwaits in those cases.
- Wait for a JS predicate:
openclaw browser wait --fn "window.ready===true"
- Wait for a selector to become visible:
openclaw browser wait "#main"
You can chain these together:
openclaw browser wait "#main" \
--url "**/dash" \
--load networkidle \
--fn "window.ready===true" \
--timeout-ms 15000
Debug workflows
If an action fails, such as with "not visible", "strict mode violation", or "covered":
- Run
openclaw browser snapshot --interactive - Switch to
click <ref>/type <ref>(in interactive mode, prefer role references) - If the problem persists, use
openclaw browser highlight <ref>to inspect what Playwright is focusing on - For odd page behavior:
- Try
openclaw browser errors --clear - Try
openclaw browser requests --filter api --clear
- Try
- For thorough troubleshooting, capture a trace:
- Start with
openclaw browser trace start - Recreate the issue
- Run
openclaw browser trace stop(this outputsTRACE:<path>)
- Start with
JSON output
--json is meant for scripting and structured automation.
Examples:
openclaw browser --json status
openclaw browser --json snapshot --interactive
openclaw browser --json requests --filter api
openclaw browser --json cookies
Role snapshots in JSON carry refs along with a compact stats section (covering lines, chars, refs, and interactive elements) so tools can gauge payload size and density.
State and environment knobs
For workflows that need "make the site behave like X", these come in handy:
- Cookies:
cookies,cookies set,cookies clear - Storage:
storage local|session get|set|clear - Offline mode:
set offline on|off - Headers:
set headers --headers-json '{"X-Debug":"1"}'(or the positional variantset headers '{"X-Debug":"1"}') - HTTP basic auth:
set credentials user pass(or--clear) - Geolocation:
set geo <lat> <lon> --origin "https://example.com"(or--clear) - Media:
set media dark|light|no-preference|none - Timezone / locale:
set timezone ...,set locale ... - Device / viewport:
set device "iPhone 14"(Playwright device presets)set viewport 1280 720
Security and privacy
- The openclaw browser profile can hold logged-in sessions, so handle it as sensitive data.
browser act kind=evaluate/openclaw browser evaluateandwait --fnrun arbitrary JavaScript within the page context, which makes them vulnerable to prompt injection. Turn this off withbrowser.evaluateEnabled=falseif it is not required.openclaw browser evaluate --fntakes a function source, an expression, or a statement body. Statement bodies get wrapped as async functions, so return the value you need withreturn. When the page-side function might exceed the default evaluate timeout, go with--timeout-ms <ms>.- For login and anti-bot guidance (X/Twitter and similar), check Browser login + X/Twitter posting.
- Keep the Gateway/node host private, either on loopback or a tailnet-only setup.
- Remote CDP endpoints carry significant power, so tunnel and secure them.
Strict-mode example (private and internal destinations are blocked by default):
{
browser: {
ssrfPolicy: {
dangerouslyAllowPrivateNetwork: false,
hostnameAllowlist: ["*.example.com", "example.com"],
allowedHostnames: ["localhost"], // optional exact allow
},
},
}
Related
- Browser - overview, configuration, profiles, security
- Browser login - signing in to sites
- Browser Linux troubleshooting
- Browser WSL2 troubleshooting