Docs
Dashboard
Examples / MoreBeta

Playground examples

The ready-made tasks and scripts of the console's Try an example list, with the same thing as an API call.

Every example here is also in the console’s playground, under Try an example. Each one shows the task or the script, a link that opens it in the playground, and the same thing as an API call in curl, Node and Python.

AI tasks run as agent runs. Scripts run next to the browser, in a session with a shell (see Scripts). They use public demo sites that allow automation, or ask for your own site or browser profile. Nothing in the playground runs until you press Run, so read a task or script before you run it.

For complete programs with a result check, see the examples.

Browser

Plain Playwright, no AI

Opens a page with plain Playwright and reads the first 5 books and their prices. No AI, so no model cost.

Needs a script, a shell.

script.js
// Plain Playwright against this browser: no AI, no model// cost. page, context and browser are already connected.await page.goto("https://books.toscrape.com");const books = await page.$$eval("article.product_pod", (items) =>  items.slice(0, 5).map((item) => ({    title: item.querySelector("h3 a").getAttribute("title"),    price: item.querySelector(".price_color").textContent.trim(),  })),);console.table(books);return books;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

An AI that asks when unsure

Looks for a present on a demo book shop and, when more than one book fits, hands the browser to you to choose, then carries on.

Needs an AI task (agent run).

Task
On https://books.toscrape.com, open the Travel category and find the books that cost less than £30. If more than one fits, don't finish yet: ask me for help to choose, and wait until I hand the browser back with my choice. Then tell me the title and price of the one I chose.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "On https://books.toscrape.com, open the Travel category and find the books that cost less than £30. If more than one fits, don't finish yet: ask me for help to choose, and wait until I hand the browser back with my choice. Then tell me the title and price of the one I chose."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"# When status is "paused", the agent is asking you. Answer in the live view, or in the note, and resume the run:curl -X POST https://api.boxline.dev/v1/agent/runs/$RUN_ID/resume \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"note": "Done, carry on."}'

When it asks, answer in the box under the steps.

Accessibility check

Runs the axe accessibility scan on your page and lists the issues it finds, worst first.

Needs a script, a shell.

script.js
// An accessibility scan of one page with axe-core. Set SITE// in Variables and credentials to check your own page.const site = env.SITE || "https://books.toscrape.com";await page.goto(site, { waitUntil: "load" });// axe-core, loaded from its public CDN and run in the page.const axeUrl = "https://cdn.jsdelivr.net/npm/axe-core@4.10.2/axe.min.js";await page.evaluate(await (await fetch(axeUrl)).text());const { violations } = await page.evaluate(() => axe.run());const order = { critical: 0, serious: 1, moderate: 2, minor: 3 };const issues = violations  .map((v) => ({ id: v.id, impact: v.impact, count: v.nodes.length, help: v.help }))  .sort((a, b) => order[a.impact] - order[b.impact]);console.log(`${issues.length} kinds of issue on ${page.url()}`);console.table(issues);return issues;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { SITE: "https://books.toscrape.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Dark pattern review

Goes through a demo shop's checkout (without placing the order) and points out confusing or deceptive steps.

Needs an AI task (agent run).

Task
Open https://www.saucedemo.com, a demo shop made for testing, and sign in with the demo username and password shown on its page. Add one product to the cart and go through the checkout up to the last step before the order is placed, using the name Test User and the postcode 10115. Don't place the order. For each step, tell me if anything is confusing or deceptive: hidden costs, pre-ticked options, fake urgency, or a hard way back.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Open https://www.saucedemo.com, a demo shop made for testing, and sign in with the demo username and password shown on its page. Add one product to the cart and go through the checkout up to the last step before the order is placed, using the name Test User and the postcode 10115. Don't place the order. For each step, tell me if anything is confusing or deceptive: hidden costs, pre-ticked options, fake urgency, or a hard way back."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Public registry lookup

Looks a company up in the UK's public company register and gives the record as JSON.

Needs an AI task (agent run).

Task
Look up the company ARM LIMITED on the UK Companies House register (https://find-and-update.company-information.service.gov.uk) and give me, as JSON: its company number, status, incorporation date and registered office address.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Look up the company ARM LIMITED on the UK Companies House register (https://find-and-update.company-information.service.gov.uk) and give me, as JSON: its company number, status, incorporation date and registered office address."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Change ARM LIMITED to the company you want.

Finds the legal name and postal address of the company behind a site, in its terms or privacy pages.

Needs an AI task (agent run).

Task
Find the legal name and postal address of the organisation behind https://www.eff.org. Look in its terms, privacy or imprint pages (linked at the bottom of most pages). Give them as JSON, with the page you found them on.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Find the legal name and postal address of the organisation behind https://www.eff.org. Look in its terms, privacy or imprint pages (linked at the bottom of most pages). Give them as JSON, with the page you found them on."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Computer use

A computer-use model drives the browser by screenshots and the mouse.

Needs an AI task (agent run), computer mode (a model with a computer-use tool).

Task
Open https://books.toscrape.com and add the cheapest poetry book to the basket.
# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Open https://books.toscrape.com and add the cheapest poetry book to the basket.",  "mode": "computer"}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Browser + shell

Download and add up

Downloads a CSV report, adds up its revenue column with Python in the shell, saves output/total.txt, and types the total into a form.

Needs an AI task (agent run), a shell.

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 form's first text box, submit it, and tell me what the page says.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "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 form's first text box, submit it, and tell me what the page says.",  "session": {    "shell": true  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Download and add up, in code

The same in code: downloads a CSV, adds it up with Python (child_process), reads the result with fs, and has the AI type it into a form with step().

Needs a script, a shell.

Before you run it, replace env.REPORT_URL with a page with a CSV link.

script.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");// Downloads land in /workspace/downloads, next to the shell.async function download(click) {  const cdp = await browser.newBrowserCDPSession();  await cdp.send("Browser.setDownloadBehavior", {    behavior: "allow",    downloadPath: "/workspace/downloads",    eventsEnabled: true,  });  let name = "";  cdp.on("Browser.downloadWillBegin", (e) => (name = e.suggestedFilename));  const done = new Promise((ok, fail) =>    cdp.on("Browser.downloadProgress", (e) => {      if (e.state === "completed") ok();      if (e.state === "canceled") fail(new Error("download canceled"));    }),  );  await click();  await done;  await cdp.detach();  return `downloads/${name}`;}// 1. The report. Set REPORT_URL (a page with a CSV link) in//    Variables and credentials; without it, a demo page is made.if (env.REPORT_URL) {  await page.goto(env.REPORT_URL);} else {  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 file = await download(() =>  page.getByRole("link", { name: /download/i }).first().click(),);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(env.FORM_URL || "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);

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { REPORT_URL: "https://example.com", FORM_URL: "https://httpbin.org/forms/post" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

PDF to summary

Downloads a PDF, turns it into text with pdftotext in the shell, and sums it up in 5 bullet points.

Needs an AI task (agent run), a shell.

Task
Download the PDF at https://arxiv.org/pdf/1706.03762 into the downloads folder, turn it into text with pdftotext in the shell, and summarise it in 5 short bullet points.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Download the PDF at https://arxiv.org/pdf/1706.03762 into the downloads folder, turn it into text with pdftotext in the shell, and summarise it in 5 short bullet points.",  "session": {    "shell": true  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Screenshots to a PDF report

Takes screenshots of 3 pages and combines them into one PDF report with Python (Pillow) in the shell.

Needs a script, a shell.

script.js
// Screenshots of 3 pages, combined into one PDF report// with Python (Pillow is installed in the shell).const { execSync } = require("child_process");const fs = require("fs");const urls = [  "https://books.toscrape.com",  "https://quotes.toscrape.com",  "https://httpbin.org",];fs.mkdirSync("output", { recursive: true });const shots = [];for (const [i, url] of urls.entries()) {  await page.goto(url, { waitUntil: "load" });  const file = `output/shot-${i + 1}.png`;  await page.screenshot({ path: file });  shots.push(file);  console.log("Saved", file, "·", await page.title());}execSync(  `python3 - ${shots.join(" ")} <<'PY'import sysfrom PIL import Imagepages = [Image.open(p).convert("RGB") for p in sys.argv[1:]]pages[0].save("output/report.pdf", save_all=True,              append_images=pages[1:])PY`,  { stdio: "inherit" },);const kb = Math.round(fs.statSync("output/report.pdf").size / 1024);return `output/report.pdf: ${shots.length} pages, ${kb} KB`;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

In code, because the AI's screenshots are for its own eyes: it can't save them as files.

Video clip

Installs yt-dlp, downloads a video you're allowed to download, and cuts its first 30 seconds into output/clip.mp4 with ffmpeg.

Needs an AI task (agent run), a shell.

Task
In the shell, install yt-dlp (pip install yt-dlp), download https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4 with it, and cut the first 30 seconds into output/clip.mp4 with ffmpeg. Tell me the clip's size and length. If the site refuses the download, stop and tell me: don't try to get around it.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "In the shell, install yt-dlp (pip install yt-dlp), download https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4 with it, and cut the first 30 seconds into output/clip.mp4 with ffmpeg. Tell me the clip's size and length. If the site refuses the download, stop and tell me: don't try to get around it.",  "session": {    "shell": true  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Only videos you own or have the rights to, or ones with an open licence.

Fill a form from a spreadsheet

Reads the rows of a CSV with Python in the shell and fills one form per row, asking you to check the first one before it submits.

Needs an AI task (agent run), a shell.

Task
In the shell, download the spreadsheet at https://httpbin.org/base64/bmFtZSxwaG9uZSxlbWFpbApBZGEgTG92ZWxhY2UsKzEgNTU1IDAxMDAsYWRhQGV4YW1wbGUuY29tCkFsYW4gVHVyaW5nLCsxIDU1NSAwMTAxLGFsYW5AZXhhbXBsZS5jb20KR3JhY2UgSG9wcGVyLCsxIDU1NSAwMTAyLGdyYWNlQGV4YW1wbGUuY29tCg== and read its rows with Python. For each of the first 3 rows, open https://httpbin.org/forms/post and fill in the customer name, telephone and email from that row. Before you submit the first one, ask me to check it and wait; after I hand the browser back, submit it and do the other rows the same way without asking again. At the end, tell me what the page said after each submit.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "In the shell, download the spreadsheet at https://httpbin.org/base64/bmFtZSxwaG9uZSxlbWFpbApBZGEgTG92ZWxhY2UsKzEgNTU1IDAxMDAsYWRhQGV4YW1wbGUuY29tCkFsYW4gVHVyaW5nLCsxIDU1NSAwMTAxLGFsYW5AZXhhbXBsZS5jb20KR3JhY2UgSG9wcGVyLCsxIDU1NSAwMTAyLGdyYWNlQGV4YW1wbGUuY29tCg== and read its rows with Python. For each of the first 3 rows, open https://httpbin.org/forms/post and fill in the customer name, telephone and email from that row. Before you submit the first one, ask me to check it and wait; after I hand the browser back, submit it and do the other rows the same way without asking again. At the end, tell me what the page said after each submit.",  "session": {    "shell": true  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"# When status is "paused", the agent is asking you. Answer in the live view, or in the note, and resume the run:curl -X POST https://api.boxline.dev/v1/agent/runs/$RUN_ID/resume \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"note": "Done, carry on."}'

The demo form only shows what it received.

Data out

Product page to JSON

Reads a product page into JSON with extract() and a schema, and saves it to output/product.json.

Needs a script, a shell.

Before you run it, replace env.URL with a product page.

script.js
// A product page as JSON: extract() reads it with the AI,// the schema says what comes back. Set URL in Variables and// credentials to use your own product page.const fs = require("fs");const url =  env.URL ||  "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html";await page.goto(url, { waitUntil: "domcontentloaded" });const product = await extract("the product on this page", {  type: "object",  properties: {    title: { type: "string" },    price: { type: "number" },    currency: { type: "string" },    inStock: { type: "boolean" },    rating: { type: "string" },  },  required: ["title", "price"],});// A copy in /workspace/output (see Files).fs.mkdirSync("output", { recursive: true });fs.writeFileSync("output/product.json", JSON.stringify(product, null, 2));return product;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { URL: "https://example.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Site to Markdown

Reads pages as clean Markdown in the browser and saves each one to output/ in the shell, ready for AI search.

Needs an AI task (agent run), a shell.

Task
Read https://quotes.toscrape.com/ and https://quotes.toscrape.com/page/2/ as Markdown. Save them in the shell as output/page-1.md and output/page-2.md, then list the files with their sizes.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Read https://quotes.toscrape.com/ and https://quotes.toscrape.com/page/2/ as Markdown. Save them in the shell as output/page-1.md and output/page-2.md, then list the files with their sizes.",  "session": {    "shell": true  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Checks the links on a page of your site (up to 40) and lists the broken and the slow ones.

Needs a script, a shell.

script.js
// Broken and slow links on one page. Set SITE in Variables and// credentials to check your own page.const site = env.SITE || "https://books.toscrape.com";await page.goto(site, { waitUntil: "domcontentloaded" });const hrefs = await page.$$eval("a[href]", (as) => as.map((a) => a.href));const links = [...new Set(hrefs)]  .filter((h) => h.startsWith("http"))  .slice(0, 40);// One request at a time, to be polite to the site.const checked = [];for (const url of links) {  const t0 = Date.now();  try {    const res = await page.request.get(url, { timeout: 15000 });    checked.push({ url, status: res.status(), ms: Date.now() - t0 });  } catch (err) {    const error = String(err.message || err).split("\n")[0];    checked.push({ url, status: null, error, ms: Date.now() - t0 });  }}const broken = checked.filter((l) => !l.status || l.status >= 400);const slow = checked.filter((l) => l.ms > 2000);console.log(`${checked.length} links: ${broken.length} broken, ${slow.length} slow`);return { checked: checked.length, broken, slow };

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { SITE: "https://books.toscrape.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

SEO and metadata audit

Checks titles, descriptions, headings, Open Graph and canonical tags on your page and the pages it links to (up to 5).

Needs a script, a shell.

script.js
// Search and sharing metadata, page by page. Set SITE in// Variables and credentials to audit your own site.const site = env.SITE || "https://books.toscrape.com";await page.goto(site, { waitUntil: "domcontentloaded" });const origin = new URL(page.url()).origin;const next = await page.$$eval("a[href]", (as) => as.map((a) => a.href));const pages = [page.url(), ...new Set(next)]  .filter((u) => u.startsWith(origin) && !u.includes("#"))  .filter((u, i, all) => all.indexOf(u) === i)  .slice(0, 6);const report = [];for (const url of pages) {  await page.goto(url, { waitUntil: "domcontentloaded" });  const m = await page.evaluate(() => {    const meta = (sel) => document.querySelector(sel)?.content?.trim() || "";    return {      title: document.title.trim(),      description: meta('meta[name="description"]'),      h1: document.querySelectorAll("h1").length,      ogTitle: meta('meta[property="og:title"]'),      canonical: document.querySelector('link[rel="canonical"]')?.href || "",      noAlt: [...document.images].filter((i) => !i.alt).length,    };  });  const fixes = [];  if (!m.title || m.title.length > 60) fixes.push("title missing or over 60 characters");  if (!m.description) fixes.push("no meta description");  if (m.h1 !== 1) fixes.push(`${m.h1} H1 headings (want 1)`);  if (!m.ogTitle) fixes.push("no og:title");  if (!m.canonical) fixes.push("no canonical link");  if (m.noAlt) fixes.push(`${m.noAlt} images without alt text`);  report.push({ url, ...m, fixes });}console.table(report.map((r) => ({ url: r.url, fixes: r.fixes.length })));return report;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { SITE: "https://books.toscrape.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Download all images

Saves every image on a page into output/images in the workspace, then zips them in the shell.

Needs a script, a shell.

script.js
// Every image on a page, saved in /workspace/output/images and// zipped. Set URL in Variables and credentials for another page.const { execSync } = require("child_process");const fs = require("fs");const url = env.URL || "https://books.toscrape.com";await page.goto(url, { waitUntil: "load" });const srcs = await page.$$eval("img", (imgs) =>  imgs.map((i) => i.currentSrc || i.src),);const images = [...new Set(srcs)].filter((s) => s.startsWith("http"));fs.rmSync("output/images", { recursive: true, force: true });fs.mkdirSync("output/images", { recursive: true });let saved = 0;for (const [i, src] of images.slice(0, 100).entries()) {  const res = await page.request.get(src);  if (!res.ok()) continue;  const ext = new URL(src).pathname.match(/\.(\w{2,4})$/)?.[1] ?? "img";  const name = `${String(i + 1).padStart(3, "0")}.${ext.toLowerCase()}`;  fs.writeFileSync(`output/images/${name}`, await res.body());  saved += 1;}execSync("cd output && rm -f images.zip && zip -qr images.zip images");const kb = Math.round(fs.statSync("output/images.zip").size / 1024);return `${saved} images in output/images, zipped: output/images.zip (${kb} KB)`;

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { URL: "https://books.toscrape.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Security headers check

Checks your site's security headers and cookie flags and lists what is missing.

Needs a script, a shell.

script.js
// Security headers and cookie flags of one page. Set SITE in// Variables and credentials to check your own site.const site = env.SITE || "https://books.toscrape.com";const res = await page.request.get(site);const headers = res.headers();const wanted = {  "strict-transport-security": "forces HTTPS",  "content-security-policy": "limits scripts and frames",  "x-content-type-options": "stops MIME sniffing",  "referrer-policy": "limits what the Referer header shows",  "permissions-policy": "turns off features the site doesn't use",};const missing = Object.keys(wanted).filter((h) => !headers[h]);const csp = headers["content-security-policy"] || "";if (!headers["x-frame-options"] && !csp.includes("frame-ancestors")) {  missing.push("x-frame-options (or CSP frame-ancestors)");}const cookies = res  .headersArray()  .filter((h) => h.name.toLowerCase() === "set-cookie")  .map((h) => ({    name: h.value.split("=")[0],    secure: /;\s*secure/i.test(h.value),    httpOnly: /;\s*httponly/i.test(h.value),    sameSite: /samesite=(\w+)/i.exec(h.value)?.[1] ?? "not set",  }));console.log(`${res.url()} answered ${res.status()}`);return { url: res.url(), status: res.status(), missing, cookies };

Open in the playground

# 1. A session with a browser and a shell (scripts run next to the browser).curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -H "Idempotency-Key: $(uuidgen)" \  -d '{"shell": true}'# 2. Run script.js in it (use the session's id; jq puts the file into JSON). The output streams back, one JSON line at a time.jq -n --rawfile code script.js '{code: $code, env: { SITE: "https://books.toscrape.com" }}' |  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 @-# 3. Stop it.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Research a question

Searches the web, reads several sources and answers with citations.

Needs an AI task (agent run), web search.

Before you run it, replace <a question> with a question.

Task
Search the web for <a question>, read the three best sources, and answer in 3 sentences with links to them.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Search the web for <a question>, read the three best sources, and answer in 3 sentences with links to them."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Logins

Save a login once

Opens your site's sign-in page and hands the browser to you to sign in. When the browser stops, the browser profile keeps you signed in for next time.

Needs an AI task (agent run), a browser profile to sign in to.

Before you run it, replace <your sign-in page> with your sign-in page.

Task
Open <your sign-in page> and ask me to sign in: I'll do it myself in the live browser. When I hand the browser back, check that I'm signed in and tell me the name shown on the page.

Open in the playground

# 1. A browser profile, and a session that keeps what happens in it (persist).curl -X POST https://api.boxline.dev/v1/profiles \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"name": "My site"}'curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"keepAlive": true, "profile": {"id": "'"$PROFILE_ID"'", "persist": true}}'# 2. Start the run (the task is the JSON's "task").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 @- <<JSON{  "task": "Open <your sign-in page> and ask me to sign in: I'll do it myself in the live browser. When I hand the browser back, check that I'm signed in and tell me the name shown on the page.",  "sessionId": "$SESSION_ID"}JSON# 3. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"# When status is "paused", the agent is asking you. Answer in the live view, or in the note, and resume the run:curl -X POST https://api.boxline.dev/v1/agent/runs/$RUN_ID/resume \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"note": "Done, carry on."}'# 4. Stop the session when you are done: this saves the profile.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

Stop the browser when you're done: the sign-in is saved when it stops.

Hand over for a code

Signs in with your username and password from Variables and credentials; when the site asks for a code (SMS or an app), the AI asks you for it, then carries on.

Needs an AI task (agent run), values for %username%, %password%.

Before you run it, replace <your sign-in page> with your sign-in page.

Task
Open <your sign-in page> and sign in with the username %username% and the password %password%. When the site asks for a verification code, ask me for it and wait, then type the code I give you. When I'm signed in, tell me the name shown on the page.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<JSON{  "task": "Open <your sign-in page> and sign in with the username %username% and the password %password%. When the site asks for a verification code, ask me for it and wait, then type the code I give you. When I'm signed in, tell me the name shown on the page.",  "variables": {    "username": "$SITE_USERNAME",    "password": {      "value": "$SITE_PASSWORD",      "origins": [        "https://example.com"      ]    }  }}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"# When status is "paused", the agent is asking you. Answer in the live view, or in the note, and resume the run:curl -X POST https://api.boxline.dev/v1/agent/runs/$RUN_ID/resume \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"note": "Done, carry on."}'

Give the code in the box under the steps, or type it in the live browser yourself.

My invoices

Opens your billing page signed in with a browser profile and downloads the invoices of the last 3 months.

Needs an AI task (agent run), a browser profile, signed in.

Before you run it, replace <your billing page> with your billing page.

Task
Open <your billing page>. I'm signed in already (browser profile). Find the invoices or billing history, download every invoice from the last 3 months, and tell me each file's name, date and amount. If you're asked to sign in, ask me for help and wait.

Open in the playground

# 1. A session signed in with your browser profile.curl -X POST https://api.boxline.dev/v1/sessions \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"keepAlive": true, "profile": {"id": "'"$PROFILE_ID"'"}}'# 2. Start the run (the task is the JSON's "task").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 @- <<JSON{  "task": "Open <your billing page>. I'm signed in already (browser profile). Find the invoices or billing history, download every invoice from the last 3 months, and tell me each file's name, date and amount. If you're asked to sign in, ask me for help and wait.",  "sessionId": "$SESSION_ID"}JSON# 3. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"# When status is "paused", the agent is asking you. Answer in the live view, or in the note, and resume the run:curl -X POST https://api.boxline.dev/v1/agent/runs/$RUN_ID/resume \  -H "x-api-key: $BOXLINE_API_KEY" \  -H "content-type: application/json" \  -d '{"note": "Done, carry on."}'# 4. Stop the session when you are done.curl -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/stop \  -H "x-api-key: $BOXLINE_API_KEY"

The files land in the browser's downloads folder: open Files on the session page to get them.

Two-factor sign-in

Signs in with a password credential that has a 2FA key: the platform makes the current code for you (%NAME.otp%).

Needs an AI task (agent run).

Before you run it, replace <your sign-in page> with your sign-in page and <credential> with your credential's name.

Task
Open <your sign-in page> and sign in with %<credential>.username%, %<credential>.password% and the code %<credential>.otp%.

Open in the playground

# 1. Start the run (the task is the JSON's "task").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 @- <<'JSON'{  "task": "Open <your sign-in page> and sign in with %<credential>.username%, %<credential>.password% and the code %<credential>.otp%."}JSON# 2. Follow it: status, steps and result (use its id).curl https://api.boxline.dev/v1/agent/runs/$RUN_ID \  -H "x-api-key: $BOXLINE_API_KEY"

Add the password and its 2FA key on the Credentials page, then pick it in Variables and credentials.

Coming soon

These examples need features that are not in the API yet. They will get their code here when they are.

  • Watch a price: Checks a product's price and stock on a schedule and sends a webhook when it changes.
  • Visual uptime check: Screenshots key pages on a schedule and flags errors or big visual changes.

Only for your own sites and accounts, demo sites, and content you may download. See the acceptable-use rules in the Terms of Service.

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