Portal Community

When to Use

  download vs. execute: Both operations run custom JavaScript. Use download when the goal is to capture a file that the browser downloads (Content-Disposition: attachment). Use execute when you want to return structured data or perform multi-step navigation without a file download.

Configuration

Connection

FieldRequiredDescription
BaseUrlRequiredBrowserless instance URL.
TokenOptionalAPI token for cloud Browserless.

Operation

FieldRequiredDescription
codeRequiredJavaScript string that triggers the file download inside the browser. The function signature is async ({ page, context }) => { ... }. Navigate to the target URL, perform the actions that trigger the download, and the Browserless service intercepts the download response and returns the file bytes.
contextOptionalJSON object passed to the function as the context argument. Use this to inject URLs, credentials, date ranges, or other dynamic values without interpolating them into the code string.
blockAdsOptionalDefault true. Block ad networks during navigation.
timeoutOptionalRequest timeout in milliseconds. Set generously — login + navigate + trigger can take 30–60 s.
useCustomBodyOptionalSend customBodyJson verbatim as the request body.
customBodyJsonOptionalRaw JSON body when useCustomBody is true.
  Security note: Never interpolate credentials or secrets directly into the code string — they appear in workflow logs. Always pass dynamic values through the context object using workflow secrets.

Sample Configuration

{
  "operation": "download",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.loginUrl, { waitUntil: 'networkidle2' }); await page.type('#username', context.username); await page.type('#password', context.password); await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle2' }), page.click('#btn-login')]); await page.goto(context.reportUrl, { waitUntil: 'networkidle2' }); await page.click('#export-csv-btn'); }",
  "context": {
    "loginUrl": "https://reports.example.com/login",
    "reportUrl": "https://reports.example.com/monthly/{{ $input.month }}",
    "username": "{{ $env.REPORTS_USER }}",
    "password": "{{ $env.REPORTS_PASS }}"
  },
  "timeout": 90000,
  "blockAds": true
}

Validation Errors

Error CodeCause
VAL_MISSING_CODEThe code field is empty.
CFG_INVALIDNode settings could not be resolved to a download configuration.
BROWSERLESS_BAD_REQUESTcustomBodyJson is not valid JSON or does not match the download request shape.

Output

Success Port

FieldTypeDescription
statusstringAlways "success".
errorCodestringAlways empty on success.
operationstringAlways "download".
mimeTypestringMIME type of the downloaded file, e.g. "text/csv", "application/pdf", "application/octet-stream".
fileNamestringFile name from the Content-Disposition header, e.g. "report-2026-05.csv".
sizeBytesintFile size in bytes.
databytesRaw file bytes. Pass to an S3 upload, email attachment, or file-write node.

Error Port

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

Sample Output

{
  "status": "success",
  "errorCode": "",
  "operation": "download",
  "mimeType": "text/csv",
  "fileName": "monthly-report-2026-05.csv",
  "sizeBytes": 18432,
  "data": "<binary CSV bytes>"
}

Expression Reference

ExpressionValue
{{ $output.BrowserlessNode.mimeType }}MIME type of the downloaded file.
{{ $output.BrowserlessNode.fileName }}File name from the Content-Disposition header.
{{ $output.BrowserlessNode.sizeBytes }}File size in bytes.
{{ $output.BrowserlessNode.data }}Raw file bytes — pass directly to upload or write nodes.

Node Policies & GuardRails

PolicyRecommendationSeverity
Code injection riskNever interpolate untrusted user input into the code string. Use the context object for all dynamic values.Critical
Credential securityAlways pass usernames and passwords via workflow secrets referenced in context — never in the code string.Critical
Token storageStore the Browserless API token in a workflow secret.Critical
File size limitsLarge file downloads (100 MB+) may exceed workflow memory limits. Validate expected file sizes and consider streaming to storage directly.Medium
Timeout for multi-step flowsLogin + navigate + trigger + wait for download can take 30–90 s. Set timeout to at least 2× the expected duration.Medium

Examples

Example 1 — Download a CSV export from a reporting portal

{
  "operation": "download",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.url, { waitUntil: 'networkidle2' }); await page.waitForSelector('#export-btn'); await page.click('#export-btn'); }",
  "context": {
    "url": "https://analytics.example.com/export?from={{ $input.from }}&to={{ $input.to }}&token={{ $env.ANALYTICS_TOKEN }}"
  },
  "timeout": 60000
}

Example 2 — Download an invoice PDF after login

{
  "operation": "download",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.loginUrl, { waitUntil: 'networkidle2' }); await page.type('[name=email]', context.email); await page.type('[name=password]', context.password); await Promise.all([page.waitForNavigation({ waitUntil: 'networkidle2' }), page.click('[type=submit]')]); await page.goto(context.invoiceUrl, { waitUntil: 'networkidle2' }); await page.waitForSelector('.download-invoice'); await page.click('.download-invoice'); }",
  "context": {
    "loginUrl": "https://billing.example.com/login",
    "invoiceUrl": "https://billing.example.com/invoices/{{ $input.invoiceId }}",
    "email": "{{ $env.BILLING_EMAIL }}",
    "password": "{{ $env.BILLING_PASSWORD }}"
  },
  "timeout": 90000,
  "blockAds": true
}

Example 3 — Download a government data export

{
  "operation": "download",
  "baseUrl": "https://chrome.browserless.io",
  "token": "{{ $env.BROWSERLESS_TOKEN }}",
  "code": "async ({ page, context }) => { await page.goto(context.searchUrl, { waitUntil: 'networkidle2' }); await page.select('#year-select', context.year); await page.click('#search-btn'); await page.waitForSelector('#download-results', { timeout: 15000 }); await page.click('#download-results'); }",
  "context": {
    "searchUrl": "https://data.gov.example.com/filings/search",
    "year": "{{ $input.year }}"
  },
  "timeout": 120000
}