Portal Community

When to Use

  Security note: The code field executes arbitrary JavaScript on the Browserless server. Use workflow secrets and the context object to pass dynamic values — never interpolate sensitive secrets directly into the code string, as that value is logged by the workflow engine.

Configuration

Connection

FieldRequiredDescription
BaseUrlRequiredBrowserless instance URL.
TokenOptionalAPI token for cloud Browserless.

Operation

FieldRequiredDescription
codeRequiredJavaScript string executed in the Browserless function context. The function signature is async ({ page, context }) => { ... }. The function can return any JSON-serialisable value, a Buffer, or a string. The Puppeteer page object is fully available.
contextOptionalJSON object passed to your function as the context argument. Use this to inject dynamic workflow values (URLs, IDs, credentials) without interpolating them into the code string.
blockAdsOptionalDefault true. Block ad networks during any page loads triggered by your code.
timeoutOptionalRequest timeout in milliseconds. Set generously for multi-step automation.
useCustomBodyOptionalSend customBodyJson verbatim as the request body.
customBodyJsonOptionalRaw JSON body when useCustomBody is true.
  Code shape: The Browserless function API expects the code body to export or return a function: async ({ page, context }) => { /* ... */ return result; }. The return value is serialised and returned as the response. Return a plain object for JSON, a string for text, or a Buffer for binary data.

Sample Configuration

{
  "operation": "execute",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.url, { waitUntil: 'networkidle2' }); const title = await page.title(); const price = await page.$eval(context.priceSelector, el => el.textContent.trim()); return { title, price }; }",
  "context": {
    "url": "{{ $input.productUrl }}",
    "priceSelector": ".product-price"
  },
  "timeout": 30000,
  "blockAds": true
}

Validation Errors

Error CodeCause
VAL_MISSING_CODEThe code field is empty.
CFG_INVALIDNode settings could not be resolved to an execute configuration.
BROWSERLESS_BAD_REQUESTThe code contains a syntax error or the request body is malformed.

Output

Success Port

FieldTypeDescription
statusstringAlways "success".
errorCodestringAlways empty on success.
operationstringAlways "execute".
contentTypestringMIME type returned by the function, e.g. "application/json", "text/plain", "application/octet-stream".
responseTypestringDiscriminator: "json", "text", or "binary".
jsonDataobjectPopulated when responseType is "json". Contains the parsed JSON return value of your function.
textDatastringPopulated when responseType is "text".
binaryDatabytesPopulated when responseType is "binary".

Error Port

FieldTypeDescription
statusstringAlways "error".
errorCodestringMachine-readable error code.
errorMessagestringHuman-readable failure description.
operationstringAlways "execute".
httpStatusCodeintHTTP status code from Browserless, if available.

Sample Output

{
  "status": "success",
  "errorCode": "",
  "operation": "execute",
  "contentType": "application/json",
  "responseType": "json",
  "jsonData": {
    "title": "Widget Pro 2026 Edition",
    "price": "$49.99"
  },
  "textData": null,
  "binaryData": null
}

Expression Reference

ExpressionValue
{{ $output.BrowserlessNode.responseType }}"json", "text", or "binary".
{{ $output.BrowserlessNode.jsonData }}Parsed JSON return value of your function.
{{ $output.BrowserlessNode.jsonData.title }}A specific field from the JSON result.
{{ $output.BrowserlessNode.textData }}Text result when responseType is "text".
{{ $output.BrowserlessNode.contentType }}MIME type of the response.

Node Policies & GuardRails

PolicyRecommendationSeverity
Code injection riskNever interpolate untrusted user input into the code string. Pass all dynamic values via the context object.Critical
Secret exposure in logsSecrets passed in the code string appear in workflow logs. Always inject credentials through context using workflow secrets.Critical
Token storageStore the Browserless API token in a workflow secret.Critical
Timeout for automationMulti-step automation (login + navigate + extract) can take tens of seconds. Set timeout accordingly — the default 30 s is often too low.Medium
Error handling in codeWrap your function body in try/catch and return a structured error object so the workflow can distinguish JS-level errors from Browserless infrastructure errors.Medium

Examples

Example 1 — Log in and extract account data

{
  "operation": "execute",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.loginUrl, { waitUntil: 'networkidle2' }); await page.type('#email', context.email); await page.type('#password', context.password); await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle2' }), page.click('#login-btn')]); const balance = await page.$eval('.account-balance', el => el.textContent.trim()); return { balance }; }",
  "context": {
    "loginUrl": "https://portal.example.com/login",
    "email": "{{ $env.PORTAL_EMAIL }}",
    "password": "{{ $env.PORTAL_PASSWORD }}"
  },
  "timeout": 60000,
  "blockAds": true
}

Example 2 — Call an internal page API and return JSON

{
  "operation": "execute",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.baseUrl, { waitUntil: 'domcontentloaded' }); const data = await page.evaluate(async (apiPath) => { const res = await fetch(apiPath); return res.json(); }, context.apiPath); return data; }",
  "context": {
    "baseUrl": "https://app.example.com",
    "apiPath": "/api/internal/metrics"
  },
  "timeout": 20000
}

Example 3 — Paginated scrape returning all items

{
  "operation": "execute",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { const results = []; await page.goto(context.url, { waitUntil: 'networkidle2' }); while (true) { const items = await page.$$eval('.item-title', els => els.map(e => e.textContent.trim())); results.push(...items); const next = await page.$('.pagination-next:not(.disabled)'); if (!next) break; await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle2' }), next.click()]); } return { items: results, total: results.length }; }",
  "context": { "url": "{{ $input.listingUrl }}" },
  "timeout": 120000,
  "blockAds": true
}