Skip to main content

Browser Tasks

The Browser/Run task drives a web portal with a real Chromium browser. Use it for carrier, customs or customer portals that have no API: sign in, fill forms, read statuses and tables, download proofs, upload documents, take screenshots, or let a model work a page toward an instruction with the llm/act verb.

The browser is not part of the API process. It runs on a browser provider (the organization's own Browserbase account, a self-hosted browser host, or any endpoint that speaks the Chrome DevTools Protocol; see Browser providers), and the task connects to it for the duration of the workflow execution.

Browser/Run​

task: "Browser/Run@1"
name: portal
inputs:
allowedDomains: ["portal.example.com"]
actions:
- do: goto
url: https://portal.example.com/login
- do: fill
target: label=Username
value: "{{ portalCreds.user }}"
- do: fill
target: label=Password
value: "{{ portalCreds.password }}"
sensitive: true
- do: click
target: role=button[name='Sign in']
- do: waitForNavigation
- do: extractText
target: css=.status
as: status
- do: screenshot
as: proof
outputs:
- name: status
- name: proof

Inputs​

InputRequiredDescription
actionsyesOrdered list of actions (below).
allowedDomainswhen any action is goto, download with url, or llm/actHosts the run may navigate to or fetch from. A bare host admits https only; http://host or https://host[:port] admits that scheme. Subdomains are not implied. Templated, so it can come from a fromConfig variable.
providernoPin a browser provider by name: the organization config tms.browser.<name>, otherwise the server provider <name>. Strict: no fallback to other providers. See Browser providers.
sessionnoreuse (default) shares one browser and page across every Browser/Run step of the execution. new opens a fresh session for this step.
timeoutnoDefault per-action timeout in seconds. Falls back to the server's Browser:DefaultTimeoutSeconds.
proxynotrue routes the session through the provider's proxy (Browserbase residential proxy); false (default) connects directly. Only Browserbase providers support it. A proxied and a direct session are never shared under session: reuse.

Actions​

Every action has do and, depending on the verb, some of target, value, url, query, file, attribute, as, all, if, sensitive, timeout, instructions, model, maxTurns.

VerbNeedsCaptures under as
gotourl (+ optional query object merged as a query string)
click, check, uncheck, hover, waitFortarget
fill, press, selectOptiontarget, value (visible label for selectOption; key name such as Enter for press)
waitForNavigation
extractTexttarget, asstring
extractAttributetarget, as, attributestring
extractTabletarget, as{ rows: [{ rowIndex, cells: { <header>: text, __inputId, __html } }], count }
extractUrlascurrent page URL
screenshotas{ name, contentType, sizeBytes, url, key } stored in file storage
uploadFiletarget, file: { url } or file: { path }
downloadas plus target (click form, optional all: true) or url (fetch form)artifact record, or an array under all
llm/actinstructions (+ optional model, maxTurns 1 to 20, as){ success, summary, turns, actions: [{ tool, selector, ok, error }] }

Common fields:

  • if is an NCalc expression over the task scope; a falsy result skips that action only.
  • timeout overrides the per-action seconds. For llm/act it bounds the whole loop.
  • sensitive: true keeps a value out of log lines and the action log. Use it for credentials and personal data.

Locators​

target must start with one of css=, xpath=, text=, label=, placeholder=, role=. Role locators accept a name: role=button[name='Sign in']. Prefer label= and placeholder= for form fields. Targets are static text; a template inside a target is rejected when the workflow is saved.

- do: fill
target: label=Tracking number
value: "{{ trackingNumber }}"
- do: click
target: role=button[name='Search']
- do: extractTable
target: css=table#results
as: results

Outputs​

The task returns every as capture, url (the page URL after the last action), and actions (a list of { index, do, target, ok, error, ms, skipped }). Map them with the step's outputs: like any other task:

outputs:
- name: status
- name: summary
mapping: "agent.summary"
- name: proofUrl
mapping: "proof.url"

Failure​

The first failing action stops the step with a message such as Browser/Run action[2] click css=button: strict mode violation. The step's continueOnError decides whether the workflow continues. Captures gathered before the failure are attached to the error.

Sessions​

The first Browser/Run step of an execution acquires a browser and a page; later steps in the same execution reuse them, so a login performed in one step is still valid in the next. The browser is released when the execution ends. A foreach over orders with one Browser/Run step per iteration therefore signs in once.

activities:
- name: portal
steps:
- task: "Browser/Run@1"
name: login
inputs:
allowedDomains: ["portal.example.com"]
actions:
- do: goto
url: https://portal.example.com/login
- do: fill
target: label=Username
value: "{{ creds.user }}"
- do: fill
target: label=Password
value: "{{ creds.password }}"
sensitive: true
- do: click
target: role=button[name='Sign in']
- task: foreach
collection: orders
steps:
- task: "Browser/Run@1"
name: lookup
inputs:
allowedDomains: ["portal.example.com"]
actions:
- do: goto
url: "https://portal.example.com/shipments/{{ item.trackingNumber }}"
- do: extractText
target: css=.status
as: status

llm/act​

llm/act puts a model in a bounded loop against the live page. The model receives the author's instruction, a page observation (title, URL, visible text capped at 6000 characters, up to 60 interactive elements with suggested selectors), and a closed tool set: readPage, click, fill, selectOption, check, uncheck, press, waitFor, goto, finish. The platform executes every tool call; the model never runs anything itself.

- do: llm/act
instructions: "Open the shipment {{ trackingNumber }} and read its delivery status."
maxTurns: 8
timeout: 120
as: agent
  • goto inside the loop obeys allowedDomains.
  • Page content is treated as data, never as instructions.
  • The captured record logs tool names and selectors only, never filled values. Keep credentials in deterministic fill actions with sensitive: true.
  • The loop ends on finish, when maxTurns is exhausted (default 8, maximum 20), or when the action timeout elapses. finish with success: false, exhaustion and timeout fail the action.
  • Without an AI backend configured on the server, llm/act fails with Llm.NotConfigured.

Credentials​

Feed a sensitive fill from a $-prefixed input or a fromConfig variable, mark it sensitive: true, and avoid logLevel: Trace on credential workflows, because Trace logs task variables.

variables:
- name: portalCreds
fromConfig:
configName: carrier-portal-credentials

Validation​

These rules run when the workflow is saved. Each code is described with causes and fixes under Browser Task Errors.

CodeRule
BROWSER_001actions is required and must be a non-empty list.
BROWSER_002Each action must be an object using only known keys.
BROWSER_003do must be a known verb.
BROWSER_004A field the verb needs is missing (for example url for goto, as for extract verbs, exactly one of file.url or file.path).
BROWSER_005A field not allowed on the verb is present (locator fields on llm/act, instructions on other verbs).
BROWSER_006target must be static text with a locator prefix.
BROWSER_007maxTurns must be an integer between 1 and 20.
BROWSER_008allowedDomains is required when any action navigates.
BROWSER_009An allowedDomains entry must be a host or scheme://host[:port] without a path.
BROWSER_010A literal goto URL must be absolute and covered by allowedDomains.

Browser providers​

A Browser/Run step never names a browser URL. It takes its browser from the organization first, then from the server:

provider inputOrganization configBrowser used
omittedtms.browser.default with an API keyThe organization's Browserbase account only. It never falls back to the server's browsers.
omittednone, or no API keyThe server's providers, tried by priority.
Xtms.browser.X with an API keyThat organization config only.
Xnone, or no API keyThe server provider named X; otherwise the step fails.

Organization Browserbase account​

An administrator connects the organization's own Browserbase account in Organization Configs, entry Browser Automation (installed with the core app). The screen writes the organization config tms.browser.default:

type: browserbase # the only provider type allowed per organization
apiKey: "${secret:org/<organizationId>/tms.browser.default.apiKey}"
projectId: "<project id>" # optional
region: us-east-1 # optional
  • The API key is stored as an organization secret; the config keeps only the reference.
  • Installing the core app creates an empty tms.browser.default in every organization. Until an API key is saved, steps keep using the server's browsers.
  • Self-hosted browser hosts and raw Chrome DevTools endpoints can only be configured on the server. An organization-supplied browser URL would let the platform connect to any address, so the organization config has no URL setting.
  • Sessions last the server's Browser:SessionTimeoutMinutes; proxy: true works if the Browserbase plan includes proxies.

To keep several accounts or projects, create further configs such as tms.browser.eu and pin them with provider: eu.

Server configuration​

Server-wide browser providers and the AI backend are configured in the server settings, not in the workflow:

"Browser": {
"DefaultTimeoutSeconds": 30,
"Providers": [
{ "Name": "chrome", "Type": "browserHost", "Priority": 0, "Endpoints": ["http://browser:8080"] },
{ "Name": "desktop", "Type": "cdp", "Priority": 1, "Url": "http://localhost:9222" }
]
},
"AI": { "Provider": "Anthropic", "Model": "claude-opus-5", "ApiKey": "<secret>" }
  • browserHost is the self-hosted headed Chromium image shipped with the backend (docker/browser). One browser per endpoint; a busy host answers 409 and the next endpoint or provider is tried.
  • cdp is any Chrome DevTools Protocol endpoint, such as a desktop Chrome started with --remote-debugging-port=9222.
  • AI:Provider is None, Anthropic, OpenAI or AzureOpenAI.

Troubleshooting​

SymptomCause and fix
could not acquire a browser: ... no browser provider is configuredThe organization has no tms.browser.default with an API key and the server has no providers. Save a Browserbase key in Organization Configs → Browser Automation.
could not acquire a browser: ... provider 'X' is not configuredThe step pins provider: X, but there is no organization config tms.browser.X and no server provider X.
could not acquire a browser: ... no browser provider could create a session (...)Every provider failed; the parentheses list each one's reason (host busy or unreachable, Browserbase rejected the key, and so on).
organization config 'tms.browser.X' has type '...'Only type: browserbase is allowed in organization config.
'https://x' is not allowed by allowedDomains (a bare entry admits https only)Add the host, or http://host for plain http.
target '#id' must start with a locator prefixUse css=#id.
strict mode violationThe locator matched several elements. Narrow it with role=button[name='...'] or a more specific css= selector.
Llm.NotConfiguredThe server has no AI:Provider.
Click-form download fails on a remote providerUse the fetch form (url), which reads the file through the page's own session.