Docs
Dashboard
Examples / MoreBeta

Browser, shell and files together

Download a file in the browser, work on it in the shell, and use the result back in the browser, all in one session: from your code, by the AI agent, and as a script.

A session with shell: true is one machine with a browser and a shell on the same disk. So the browser can download a file, the shell can work on it, and the browser can use the result, without the file ever leaving the machine.

One disk for everything

PartHow it uses /workspace
BrowserEvery download lands in /workspace/downloads, whoever clicks: your code, the agent, or a person in the live view. The upload action puts a file from the workspace into a form.
ShellStarts in /workspace. downloads/report.csv is the file the browser just saved.
Files APIReads, writes, lists and waits for the same files, from your own code. See File uploads and downloads.

The examples below download a small revenue report, add up its revenue column with Python, keep the total in output/total.txt (5460), and type it into a form. They run as they are: the report is a demo file and the form is httpbin.org’s demo form. For your own site, open your report page and your form instead. The shell machine already has Python with pandas, Node, ffmpeg, ImageMagick and poppler.

1. From your code

Playwright drives the browser over the session’s connectUrl. The files API waits for the download, exec runs Python in the shell, the files API reads the result, and the actions API fills in the form. The curl version uses actions instead of Playwright for the browser part.

# 1. A session with a browser and a shell.curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"shell": true, "keepAlive": true}'# 2. Browser: put a download link on the page and click it (on your own site: goto your report page).curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/browser/actions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d @- <<'JSON'{"actions": [  {"action": "evaluate", "expression": "document.body.innerHTML = '<a download=\"report.csv\" href=\"data:text/csv,month%2Crevenue%0AJan%2C1200%0AFeb%2C1850%0AMar%2C2410\">Download report</a>'"},  {"action": "click", "selector": "text=Download report"}]}JSON# 3. Files: wait for the download in /workspace/downloads.curl -G https://api.boxline.dev/v1/sessions/$SESSION_ID/files/wait \  -H "x-api-key: $BOXLINE_API_KEY" \  --data-urlencode "pattern=downloads/*.csv"# 4. Shell: add it up with Python, on the same machine.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/shell/exec \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d @- <<'JSON'{"command": "mkdir -p output && python3 -c \"import pandas as pd; print(pd.read_csv('downloads/report.csv')['revenue'].sum())\" > output/total.txt"}JSON# 5. Files: read the result (5460).curl -G https://api.boxline.dev/v1/sessions/$SESSION_ID/files \  -H "x-api-key: $BOXLINE_API_KEY" \  --data-urlencode "path=output/total.txt"# 6. Browser: type it into the form and send it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/browser/actions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"actions": [        {"action": "goto", "url": "https://httpbin.org/forms/post"},        {"action": "fill", "selector": "input[name=custname]", "value": "5460"},        {"action": "click", "selector": "form button"},        {"action": "content", "format": "text"}      ]}'

2. With the AI agent

Give the run a session with a shell (session: { shell: true }) and a goal that needs both. The agent uses its browser tools for the web pages and bash for the calculation.

curl -X POST https://api.boxline.dev/v1/agent/runs \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{    "task": "Download the report from https://httpbin.org/base64/bW9udGgscmV2ZW51ZQpKYW4sMTIwMApGZWIsMTg1MApNYXIsMjQxMAo= (a CSV file). In the shell, add up its revenue column with Python and save the total, and nothing else, to output/total.txt. Then open https://httpbin.org/forms/post, type the total into the first text box of the form, submit it, and tell me what the page says.",    "session": {"shell": true}  }'# Then poll GET /v1/agent/runs/$RUN_ID until status is "completed" or "failed".

The run’s steps show the switch from browser to shell and back. An example, shortened (each run picks its own way):

run.steps (excerpt)
[  { "type": "tool", "name": "browser_navigate", "input": { "url": "https://httpbin.org/base64/bW9udGgs…" } },  { "type": "tool", "name": "bash", "input": { "command": "curl -s -o downloads/report.csv 'https://httpbin.org/base64/bW9udGgs…'" } },  { "type": "tool", "name": "bash", "input": { "command": "python3 -c \"import csv; …\" > output/total.txt && cat output/total.txt" }, "output": "5460" },  { "type": "tool", "name": "browser_navigate", "input": { "url": "https://httpbin.org/forms/post" } },  { "type": "tool", "name": "browser_type", "input": { "element": 1, "text": "5460" } },  { "type": "tool", "name": "browser_click", "input": { "element": 12 } },  { "type": "text", "text": "The total revenue is 5460. I saved it to output/total.txt and submitted the form; the page shows custname: 5460." }]

Each tool step also has output, ms and at. See Follow a run.

3. In a script

A script runs with Node inside the session, next to the browser. Besides Playwright, it can require() Node’s own modules: child_process to run shell commands and fs for the workspace. step() handles the clicks you would rather describe than select, and useModel() picks its model.

total.js
// Download a CSV, add it up with Python in the shell, and// type the total into a form. One AI step; the rest is code.const { execSync } = require("child_process");const fs = require("fs");useModel("claude-haiku-5-5"); // the model step() uses// 1. The report (a demo page; on your own site, page.goto() it).const csv = "month,revenue\nJan,1200\nFeb,1850\nMar,2410";await page.setContent(`<h1>Quarterly report</h1>  <a download="report.csv" href="data:text/csv,${encodeURIComponent(csv)}">Download report</a>`);const [download] = await Promise.all([  page.waitForEvent("download"),  page.getByRole("link", { name: /download/i }).first().click(),]);fs.mkdirSync("downloads", { recursive: true });const file = `downloads/${download.suggestedFilename()}`;await download.saveAs(file);console.log("Downloaded", file);// 2. Add up the revenue column with Python, in the shell.fs.mkdirSync("output", { recursive: true });execSync(  `python3 - "${file}" <<'PY'import csv, sysrows = csv.DictReader(open(sys.argv[1]))total = sum(float(r["revenue"]) for r in rows)open("output/total.txt", "w").write(f"{total:g}")PY`,  { stdio: "inherit" },);const total = fs.readFileSync("output/total.txt", "utf8").trim();console.log("Total revenue:", total);// 3. The AI types it into the form; code submits it.await page.goto("https://httpbin.org/forms/post");await step(`type ${total} into the first text box of the form`);const before = page.url();await page.locator("form button, form [type=submit]").first().click();await page.waitForURL((u) => u.href !== before);return (await page.locator("body").innerText()).slice(0, 400);
# The body is JSON: put the script (total.js, above) in "code".jq -n --rawfile code total.js '{code: $code, timeoutMs: 300000}' |  curl -N -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/browser/script \    -H "x-api-key: $BOXLINE_API_KEY" \    -H "content-type: application/json" \    -d @-

See Scripts for what else is in scope.

More examples

Tested examples that use the browser, the shell and the files together, each with its result check:

Good to know

  • Read what you want to keep before you delete the session: its files go with it. Stopping keeps them, and you can still read them or download a whole folder afterwards. See Stop and resume.
  • To use the browser’s login from the shell (for curl or wget), export its cookies. The file holds live credentials: delete it when you are done.
  • Packages you install with setup are installed again after a resume. Only /workspace is saved. See Install packages.
  • Shell sessions are billed by their machine size, not as a browser session. See Pricing.

The same flow from Python with boxline or Node with Boxline: the Node SDK and Python SDK pages list every method.

These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.