CHANGELOG

What's new in the box.

New features, fixes and breaking changes in Boxline, newest first.

Filter entries by tag

NEW

OpenRouter, a model key for one run, and Claude prompt caching

Three ways to run agents on your own model terms. See Your own model keys.

  • OpenRouter is a fifth provider, on your own OpenRouter key. Name any OpenRouter model, such as anthropic/claude-sonnet-4.5. A run’s cost is what OpenRouter reports it charged. Tools mode only.
  • A model key for one run. Send modelKey with an agent run, extract, or a step, and only that request uses it. It is never stored, there is no model charge from Boxline, and the run says keySource: "run". Resuming a run that stopped early needs the key again. Tasks keep using your project’s keys.
  • Claude prompt caching on your own Anthropic key: promptCaching "5m" or "1h" in Settings, Models, or for one run. Runs report cacheReadTokens and cacheWriteTokens, and runs with many steps cost less.
  • In the SDKs and the CLI. @boxline/sdk and boxline-sdk 3.3.0 take all three: modelKey (model_key), promptCaching (prompt_caching) and the openrouter provider. @boxline/cli 2.1.0 adds --provider on run and extract, and reads a key for one run from the environment variable BOXLINE_MODEL_KEY, never from an option. See the CLI.
IMPROVED

WebGL in the browser, OpenGL in the shell, and fixed disk sizes

Session machines do more, and fail clearly when they are full.

  • WebGL 1 and 2 work in session browsers, and the shell has OpenGL through Mesa, for programs that render by themselves. Both render in software on the machine’s CPUs. See Graphics without a GPU.
  • Fixed disk sizes. /workspace holds 10 GiB (5 GiB in a browser-only session), and installed packages about 4 GiB. A full disk fails the write at once with “No space left on device”. See The workspace.
  • Session changes always reach the machine. A proxy change applies on every API server, and a new type, place or server also gets a new IP. A new CAPTCHA mode, clock, language or cookie-banner setting applies within about 3 seconds.
CHANGED

A simpler API: one call per job, and one call to change a session

Changed The API is regrouped so that every job has one obvious call, and a thing you change is changed with the same field names you created it with. This is a breaking change with no aliases: there were no customers on the old routes yet, so they are gone. A call on an old route answers 404 not_found, and an old field answers 400 invalid_request and names the field to use. Upgrade to @boxline/sdk 3.0.0, boxline-sdk 3.0.0, @boxline/mcp 3.0.0 and @boxline/cli 2.0.0. New here? Read Start here: eight calls, each as the smallest request that works.

What is new

  • Start here. The API reference opens with the eight calls most things begin with: fetch a page, search, start a session, drive its browser, run a command, read a file, stop it, or hand the job to an AI. The reference groups follow the same order: Web, Crawls, Sessions, Browser, Shell, Files, Logs and recording, Agent, Tasks and the rest.
  • Three kinds of session, one way to ask. A session has a browser only (the default), a shell only ({"browser": false, "shell": true}) or both ({"shell": true}). Without a browser, connectUrl and liveUrl are null, and the browser calls and the live view answer 409 browser_disabled. See Configuring sessions.
  • One call to change a running session. PATCH /v1/sessions/:id takes timeout (more time), rotateProxy: true (a new proxy IP), rotateUrls: true (new signed URLs), and the settings you can already change. What is fixed when a session is created answers 400 not_updatable and names the field. See Lifecycle.
  • Get a session gives the live view link. GET /v1/sessions/:id returns fresh signed connectUrl, liveUrl and terminalUrl every time.
  • Files. GET /v1/sessions/:id/files/archive downloads a folder as one .tar.gz: everything an agent made, in one call. Reading a file, listing a folder and downloading a folder also work on a stopped session, without starting a machine, until it is deleted. See Files.
  • Agent runs and tasks take a session. The run’s own session is session, with the same fields as Create a session, so an agent can work in a shell only too. A task takes session as well. See AI agent.
  • Pause and Resume, one run. POST /v1/agent/runs/:id/pause waits for a person (a CAPTCHA, a 2FA code, a choice) on every kind of run. POST /v1/agent/runs/:id/resume is the one call to keep a run going: after a pause, or after it stopped early at a limit or a restart. The same run goes on, with one run id, and its steps and usage add up. See AI agent.
  • Commands from everyone in the session events. Every shell command is an exec event whoever ran it, with data.by (api, agent, setup or script) and the runId for an agent. See Recordings.
  • Crawls. The page ceiling follows your plan: maxPages is up to 200 on Free and up to 1000 on paid plans. See Crawl.
  • Rotating secrets is an update. rotateSecret: true on a webhook endpoint and rotateCodeUrlSecret: true on a credential make the new secret, shown once, in the same PATCH you use for every other change.
  • One usage call, and the Trajectories choice in the settings. GET /v1/project/usage has what usage and stats had. trajectories is part of GET and PUT /v1/project/settings.
  • MCP: 26 tools. New: browser_act (steps in plain English or as actions) and session_list; session_create takes browser, so an MCP client can make a shell-only session. The tools are named by what they work on: browser_*, shell_exec, files_*, web_* and session_*. See the tools.
  • CLI 2.0. boxline resume keeps a run going, and boxline run --no-browser starts a shell-only run. boxline files get saves a folder as .tar.gz.

What to change in your API calls

  • The browser calls moved under /browser: /actions, /computer, /scripts/run and /browser/cookies/export are now /browser/actions, /browser/computer, /browser/script and /browser/export-cookies. POST /v1/sessions/:id/exec is POST /v1/sessions/:id/shell/exec.
  • POST /v1/sessions/:id/extend is PATCH /v1/sessions/:id with {"timeout": seconds}. POST …/proxy/rotate and POST …/rotate-urls are rotateProxy: true and rotateUrls: true in the same PATCH. GET /v1/sessions/:id/live is GET /v1/sessions/:id.
  • POST /v1/sessions/:id/move is removed, and so is moves on a session. Nothing replaces it: you never need to choose a machine. If a machine is lost, the platform starts the session again on a new one by itself.
  • /v1/crawl is /v1/crawls (start, list, get and cancel). GET /v1/webhooks/events is GET /v1/webhooks/event-types. /v1/api-keys is /v1/project/api-keys.
  • GET /v1/usage and GET /v1/stats are GET /v1/project/usage. GET and PUT /v1/project/trajectories are the trajectories field of /v1/project/settings.
  • POST /v1/agent/runs/:id/takeover is /pause, and /handback and /continue are both /resume. GET /v1/agent/runs/:id/events is /events/stream. On a run, continuable is resumable, and continuedFrom and continuedBy are gone: a resumed run is the same run. The error not_continuable is not_resumable.
  • POST /v1/agent/runs no longer takes browser, shell, proxy, captcha, blockAds, cookieBanners, extensions, profile, timeout or idleTimeout at the top level: put them in session. A task’s browser and profile are session too (tasks you saved are converted); allowWithExtensions is a field of the task itself.
  • POST /v1/webhooks/:id/rotate-secret and POST /v1/credentials/:name/code-url-secret are rotateSecret: true and rotateCodeUrlSecret: true in a PATCH.
  • Lists take after only: offset is 400 invalid_request. A shell-only session has no live view, and a call that needs a browser on one answers 409 browser_disabled (it ran on a Chrome nobody could see before).

What to change in the SDKs, the CLI and the MCP server

  • Node and Python 3.0.0. Removed: sessions.move, sessions.extend, sessions.rotateProxy, sessions.rotateUrls, sessions.live, agent.continueRun, stats, webhooks.rotateSecret, credentials.rotateCodeUrlSecret, project.trajectories, project.setTrajectories and the session.browser and session.shell groups. Use sessions.update (or session.update) with timeout, rotateProxy and rotateUrls, sessions.get for the URLs, webhooks.update with rotateSecret, credentials.update with rotateCodeUrlSecret, bx.usage(), project.settings(), and session.exportCookies() and session.restartShell(). Renamed: agent.takeover is agent.pause, and agent.handBack is agent.resume (Python hand_back is resume). New: sessions.files.archive, and Python session.on_captcha.
  • CLI 2.0.0. boxline continue is boxline resume, and the same run goes on instead of a new one starting.
  • MCP 3.0.0. Renamed: run_command is shell_exec; list_files, read_file, write_file and delete_file are files_list, files_read, files_write and files_delete; fetch_url, screenshot_url, extract_data, crawl_site and crawl_results are web_fetch, web_screenshot, web_extract, web_crawl_start and web_crawl_get; computer is browser_computer. key is part of browser_press. Removed: session_move, hover, mouse_move, mouse_click, mouse_drag and run_playwright (browser_act, browser_click and browser_computer do their work). There are no tools for agent runs: an MCP client is already an agent.
NEW

Connect ChatGPT and Claude without an API key, five new MCP tools, and the Boxline plugin

You can now connect Boxline to ChatGPT, Claude and Claude Code with a Boxline sign-in: choose Connect, sign in, pick a project and allow it. There is no API key to create or paste. The hosted server at https://mcp.boxline.dev/mcp supports it, and @boxline/mcp 2.1.0 adds five tools. Read Connect Boxline to ChatGPT, Claude and Claude Code.

What is new

  • Connect with a sign-in. In Claude Code: claude mcp add --transport http boxline https://mcp.boxline.dev/mcp, which opens your browser. In ChatGPT and Claude, add Boxline as a connector and choose Connect. API keys in the Authorization header still work, and they are the way to use saved credentials.
  • What a connected app can do. It can use sessions, files, fetch, search, screenshots, extract and crawls, in the project you picked and with your role in it. It cannot use API keys, billing, members or saved credentials. It gets the directory tools: no browser_login and no credentials_list.
  • Tokens. An access token lasts 1 hour, and the refresh token is replaced every time it is used. Only hashes of them are stored.
  • Connected apps. Settings → Connected apps in the console lists the apps you connected, with the project and when each was last used. Disconnect ends a connection at once at the API.
  • Five new tools. crawl_site and crawl_results crawl a site and page through the result, extract_data returns JSON from up to 10 pages, screenshot_url takes a screenshot without a session, and delete_file removes a file from a session’s workspace. The first four need no session. See the tools.
  • Tokens are checked first. The hosted server asks the API whether a token is good before it serves a request, and remembers the answer for a minute. An expired or revoked token gets 401, so an app signs in again instead of getting a tool error.
  • The Boxline plugin brings the connection and five skills (web-research, crawl-site, scrape, browse and sandbox) to ChatGPT, Codex, Claude and Claude Code. In Claude Code, add it with claude plugin marketplace add Boxline-dev/plugin and claude plugin install boxline@boxline. In the ChatGPT and Claude directories, it comes once it is listed.
  • Privacy policy. A new section, “AI apps you connect to Boxline”, says what a connected app sends us, why, who receives it, how long we keep it and how to disconnect. Read the privacy policy.

Safe use

  • Before you choose Allow, check the app’s name and the address it sends you back to. A warning means Boxline does not know the app.
  • A connected app can start sessions, and sessions are billed while they run. Pick a project with a spending limit, and ask the assistant to stop sessions when it is done.
  • Disconnect apps you no longer use, under Settings → Connected apps.
CHANGED

MCP server 2.0: a hosted endpoint, and no default session

Changed Boxline now hosts its MCP server at https://mcp.boxline.dev/mcp, so a client can connect with a URL and your API key and install nothing. The package moves to @boxline/mcp 2.0.0, which has one breaking change: there is no default session any more, and every tool that works in a session takes a required sessionId. Read MCP.

What is new

  • The hosted server. Streamable HTTP at https://mcp.boxline.dev/mcp, with your API key as Authorization: Bearer <API key>. In Claude Code: claude mcp add --transport http boxline https://mcp.boxline.dev/mcp --header "Authorization: Bearer …". The server keeps no key of its own, and over HTTP the results leave out the live view URL, because anyone who has it can control the browser. Request bodies are limited to 4 MiB.
  • Host it yourself. npx -y @boxline/mcp --http serves the same tools at POST /mcp, with MCP_PORT, MCP_ALLOWED_HOSTS, MCP_TOOLS and MCP_MAX_IN_FLIGHT. MCP_TOOLS=directory leaves out browser_login, credentials_list and every credential input, for a server listed in a public connector directory.
  • Tool annotations. Every tool has a title and read-only, destructive and open-world hints, so a client can decide what to ask you about. Each tool’s page in the reference lists them.
  • MCP SDK 2, revision 2026-07-28. Clients that speak the 2025 protocol keep working.
  • session_create also says whether the session has a shell. When you do not ask for a shell and your plan or the API does not allow one, it creates a browser-only session.

What to change

  • Call session_create first, and pass the sessionId it returns to every other tool that works in a session. A call without it is refused. session_create, credentials_list, fetch_url and web_search need no session.
  • A stopped session: the tools that act on it resume it first, as it was. The read-only tools (browser_read, browser_screenshot, list_files, read_file) do not, because a resume starts a machine and billing. They say the session is stopped, and the model calls session_resume.
  • The server needs Node 20 or newer.

Safe use

  • The hosted server uses the key of each request, so it can do only what that key can. Use a key just for MCP, keep the client’s config file out of shared repositories, and revoke the key when you stop using it.
  • If you host the server yourself, put it behind HTTPS and list your host name in MCP_ALLOWED_HOSTS. Do not set it to * on a server that is open to the internet.
CHANGED

Stop, resume and delete replace release and pause

Changed A session now has three actions: stop saves it exactly as it is and frees the machine, resume brings it back, and delete ends it for good. release and pause are gone. This is a breaking change with no aliases: the old routes, statuses, fields and webhook events are removed. Upgrade to @boxline/sdk 2.0.0, boxline-sdk 2.0.0, @boxline/mcp 1.0.0 and @boxline/cli 1.0.0 when they are out. Read Lifecycle.

What is new

  • POST /v1/sessions/:id/stop saves the whole browser (every tab with its history, cookies and site storage) and the files in /workspace, frees the machine and stops billing. A stop carries a browser of up to 1 GB compressed (it was 256 MB).
  • POST /v1/sessions/:id/resume brings a stopped session back on a fresh machine with the same id. You can stop and resume it any number of times. Running processes and page memory do not come back, and pages reload. It answers 409 nothing_saved when nothing was saved.
  • DELETE /v1/sessions/:id deletes the session now, with what it saved, its recording and its logs. It cannot be undone. The record of its usage and cost stays for billing.
  • Everything that used to end a session now stops it, saved: its timeout, an idleTimeout, no clients, an agent run finishing, a spending limit and a lost machine. So a session that timed out can be resumed.
  • A stopped session is free and does not count towards your concurrent sessions. It is kept for your plan’s retention days (7 on Free, 30 on Hobby and Startup, 90 on Scale), counted from the stop, and then deleted. deletesAt says when. Stop and resume work on every plan.
  • A stop rotates the session’s signed URLs (as rotate-urls does), so a link you shared before it stays refused after a resume, and resume returns fresh ones. Stop, resume and delete are limited to 60 calls a minute per project; a bulk call counts as one.

What to change in your code

  • release() becomes stop() (to keep the session) or delete() (to be rid of it). pause() becomes stop(). bulk takes "stop", "resume" or "delete". Leaving a Python with block stops the session.
  • A call on a stopped session no longer resumes it. Actions, exec, files, live view and connect answer 409 session_not_running: call resume() first. An agent run given a stopped sessionId resumes it itself. Resuming a deleted session answers 409 session_not_stopped.
  • status is RUNNING, STOPPED, DELETED or ERROR (a session that never started). PAUSED and COMPLETED are gone.
  • stoppedAt and stopReason replace endedAt and endReason. New: deletesAt, deletedAt and deleteReason (requested or retention). dataDeletedAt is gone.
  • The session.stopped and session.deleted webhooks replace session.ended. An endpoint that listened for it has to listen for both. session.expiring stays.
  • The plan feature pauseResume is gone, and pausedSessions in the prices became stoppedSessions (free).
  • The CLI: sessions release becomes sessions stop, with sessions resume and sessions delete, and sessions list --status takes running, stopped, deleted and error. The MCP server: session_close becomes session_stop, with session_resume and session_delete.

Sessions that already exist

  • Paused sessions are now stopped, and keep what they saved.
  • Completed sessions are now stopped with nothing saved, so a resume answers 409 nothing_saved. They are deleted after your plan’s retention days, counted from when they ended. Sessions whose recordings and logs were already deleted show as deleted.

Safe use

  • A stopped session’s saved browser holds its sign-ins until it is deleted. Delete sessions you will not resume, and keep only the ones you need. To keep just a login, use a browser profile.
  • A delete cannot be undone. Read anything you want to keep from /workspace, and download recordings and logs, first.
IMPROVED

The whole browser moves with a session, and starts wait for a machine

A pause, a move and a recovery now carry the whole browser, not only its cookies and open tabs. Your agent stays signed in, and its tabs come back with their history. When no machine is free, starting a session now waits for one instead of failing at once. Nothing in your code needs to change. Read What a session keeps.

The whole browser moves

  • Pause and resume, move, and recovery from a checkpoint carry every tab with its back and forward history, the history list, cookies (session cookies too), every site’s localStorage and sessionStorage (also of tabs you closed), IndexedDB, service workers, form values and scroll positions.
  • Sites that keep a sign-in in IndexedDB, or in a closed tab’s storage, stay signed in.
  • The browser’s caches are not carried: pages load from the network again. Pages reload, so their scripts start again.
  • When the browser cannot move whole (over 256 MB compressed, a machine from an older version, or a failed copy), the session continues as before with its cookies and the storage of its open tabs. A lifecycle warning says why: data.browserProfile is too_large, machine_too_old or failed, and data.during says when. A checkpoint warns once per session and reason. See When the browser cannot be saved whole.
  • The resumed, moved and recovered… events show how the browser came back in data.browserProfile: tabs, storage or none. The paused event has it as true or false, and moved has profileMs.
  • A move closes the browser while it copies it (about a second). A client that connects in that time waits until the move is done.

Safe use: sign-ins and secret variables

  • A paused session’s stored browser holds its sign-ins. It is deleted when the session ends, so release sessions you will not resume.
  • A pause or checkpoint taken while an agent run with secret variables works stores the browser without its history, the tabs to restore, autofill and saved passwords. Tabs reopen at their addresses without query strings. A move carries the browser whole.
  • After the run, the history is carried again, so an address that a page put a value into stays in it. Use a session of the run’s own (the default) when that matters. See Hidden everywhere.

When no machine is free

  • With no idle machine, a start now waits up to 110 seconds while the platform adds one (about 70 seconds when a new server has to start) before it answers 503 no_capacity. It applies to starting, resuming and moving a session, and to the web APIs that open a browser.
  • Keep your client’s time limit above 110 seconds. The SDKs use 120 seconds per request. See When no machine is free.
NEW

Credit and spending limits

Your organization now has a credit balance, and its owners, admins and billing members can set monthly spending limits. Read Credit and spending limits. Buying credit is coming. Until then, credit is your plan’s included credit plus credit we grant, and using it up does not stop your work: you get an alert.

What you get

  • Included credit is used first. It starts over on the 1st (UTC) and never carries over. Granted credit comes next, the credit that expires first used first.
  • A monthly spending limit for the organization, and one for each project. At a limit, new sessions, agent runs, extract, steps, fetch, screenshot, PDF, crawl and fresh searches get 402 spend_limit, and running sessions stop within about a minute. Raising the limit lets work start again at once.
  • Alerts by email to owners and billing members at 50%, 80% and 100% of a limit, when credit runs low, and when it is used up. The usage.limit_reached webhook is sent at 100% and when credit is used up.
  • The console’s usage page shows spend by month, project, API key and kind, the balance, and each limit. Credit, usage and limits are for the console: an API key cannot read or change them, so a leaked key cannot raise a limit.

What changed in the API

  • 402 spend_limit can now mean the organization’s or a project’s spending limit, as well as your plan’s model spend cap or proxy data allowance. The message says which. There is a new code, 402 out_of_credit, for refusing work when credit is used up. It is not switched on: today, running out of credit only sends an alert.
  • A session’s endReason can be spend_limit or out_of_credit. An agent run working in a stopped session fails with errorCode: "session_ended".
  • usage.limit_reached has three new kind values: spend_limit, project_spend_limit and credit.
  • The audit list has new actions: billing.limit_changed, billing.alert, credit.granted, credit.adjusted, plan.private_set and plan.private_removed, and via can be system. See Organizations, projects and team members.
  • A plan has organizationId: null, except on a custom plan made for one organization.
  • The SDKs return these values as they are. Their types list the new ones from their next release: until then, compare endReason and kind as plain strings.
NEW

Organizations, projects and team members

Your account now belongs to an organization. An organization holds projects and members, so you can invite your team, give each person a role, and keep work apart in separate projects. Your API keys, the SDKs, the CLI and the MCP server work as before. Read Organizations, projects and team members.

Organizations, projects and members

  • Every account has a personal organization, made at sign-up with a first project and API key. Accounts that already existed got one that holds their project, with the same plan. You cannot create more organizations yet; you join one when someone invites you.
  • The plan, its limits and features, and a suspension belong to the organization. Running sessions, model spend, proxy data, CAPTCHA solves and web searches are added up over all its projects. A project can have a lower concurrencyCap on its running sessions. An organization has at most 20 projects.
  • Owners and admins create, rename, limit and delete projects. Deleting one revokes its API keys, switches off its webhooks and schedules, and deletes its credentials, model keys, browser profiles and extensions. The history and usage of its sessions stay.
  • Five roles: owner, admin, developer, viewer (reads only, and gets no machine URLs) and billing (no project data). Developers and viewers reach all projects or chosen ones.
  • Invite people by email. The link works for 7 days, once, and only for the invited email, and the person inviting needs a confirmed email. Members can leave, owners and admins can remove them, and an owner can transfer ownership. An organization always has an owner.
  • Owners and admins read an audit list of membership, invitation, role, project and API key changes. The console has a switcher for the organization and project you work in, a Projects page, and Settings, Organization.

What changed in the API

  • GET /v1/auth/me has organization (with a key, its project’s, and role is null), organizations (null with a key), project.orgId, project.concurrencyCap, user.emailConfirmationRequired and user.emailConfirmationAvailable.
  • GET /v1/api-keys has createdBy on each key: how it was made, by whom. null for older keys.
  • POST /v1/auth/signup takes inviteToken and answers with organization. A 429 concurrency_limit can now mean the organization’s limit or the project’s cap, and the 402 messages for monthly allowances say “this organization”.
  • New error codes: role_forbidden, email_unconfirmed, impersonating, organization_suspended, no_project, last_owner, organization_not_empty, project_has_sessions, already_member, invite_pending, invite_not_found, invite_email_mismatch, invite_expired, invite_withdrawn, invite_used, invalid_confirmation_code, confirmation_code_expired, confirmation_code_locked and mail_unavailable. See Errors and request ids.
  • New rate limits for invitations, new projects, organization changes and confirmation codes.

Confirm your email with a code

  • Signing up now needs a 6-digit code, emailed to your address. It works once, for 15 minutes. After 5 wrong tries at one code, ask for a new one. After 15 wrong tries over all of an account’s codes (403 confirmation_locked), only a password reset confirms the email. At most 10 codes are sent to an account in a day (429 rate_limited).
  • Signing in never asks for a code. A new sign-up that has not entered its code is held back, also when it comes back later: it can sign in only to confirm. Other console calls and creating an API key answer 403 email_unconfirmed. API keys you already have keep working.
  • Accounts made before this are never held back. They need a confirmed email only to invite people or transfer an organization, and can confirm it whenever they like. A password reset or an accepted invitation also confirms the email.
  • GET /v1/auth/me has user.emailConfirmationRequired (true only for a held-back sign-up) and user.emailConfirmationAvailable.
  • A password reset of an account whose email was never confirmed now recovers every organization you own, and your personal one: it revokes the keys of their projects, switches off webhooks and schedules, removes the other members and withdraws pending invitations. Confirm your email so a reset keeps your keys. See Confirm your email.

Using it safely

  • Give people the lowest role that works, and chosen projects when they need only one.
  • An API key has no role, and removing a member does not revoke the keys they made. Check createdBy in the key list and revoke the ones that should stop.
  • Deleting a project revokes its keys at once and deletes its credentials and profiles for good.
NEW

More 2FA and a login action

Sites that email or text a 2FA code, or send a sign-in link, now work with a saved password. Your own system sends the code, and the AI types it without seeing it. A new action signs a session in with one call. Passwords you saved before keep working: one with a 2FA key has codeSource "totp".

2FA codes from your own system

  • A password’s codeSource is "totp" (an authenticator key; totpSecret on its own still means this), "push" (your system sends each code or sign-in link) or "url" (the platform asks an address of yours). codeTimeoutSeconds (5 to 900, 300 by default) is how long a run waits.
  • POST /v1/credentials/:name/codes with { code } or { link } sends one. It is used once, by a wait that began before it arrived, and kept for 10 minutes.
  • With codeUrl, a public HTTPS address of yours, the platform sends a signed POST every 5 seconds while a run waits, and you answer with the code or 204 for “not yet”. The request carries Boxline-Signature, made with the credential’s codeUrlSecret (shown once; a new one with POST /v1/credentials/:name/code-url-secret), and you check it like a webhook.
  • %NAME.otp% now waits for a fresh code in agent runs, plain-English steps, scripts, the type action and boxline-otp. %NAME.link% is a sign-in link that the agent opens with its browser_open_link tool, only on the credential’s sites. The model never sees a code or a link.
  • A run that waits records code steps, and the webhook credential.code_needed tells your system to send the code. The audit log has a new action, code.
  • Changing codeSource or codeUrl needs the password again, as a new site does. New errors: credential_code_timeout, credential_link_wrong_site and code_url_not_allowed.
  • Use it safely. Send codes from your own server over HTTPS, never through a chat or a log. Check Boxline-Signature on every request to your codeUrl, and keep its secret in your secret manager: an endpoint that answers anyone hands out your codes. See 2FA codes and sign-in links.

The login action

  • { action: "login", credential, url? } on POST /v1/sessions/:id/actions signs the browser in with a password credential. A short agent run does it, with only that credential, on its sites, and the answer is { url, title, runId }. Without credential it uses the one the session’s profile links.
  • It stops after 15 steps, plus the credential’s codeTimeoutSeconds when the codes come from you, and answers credential_login_timeout. A login that fails answers credential_login_failed with the run’s id. It counts as an agent run, and its model use counts like any run’s.
  • When the page is one a sign-in link opened, the answer shows only its site, never a path that may hold the link’s token. See Sign in in one call.

Coming in the packages

  • Node SDK 1.3.0 and Python SDK 1.3.0: credentials.pushCode / push_code, rotateCodeUrlSecret / rotate_code_url_secret, session.login, codeSource, codeUrl and codeTimeoutSeconds on credentials.create and update, and the errors CredentialCodeTimeoutError, CredentialLinkWrongSiteError, CredentialLoginFailedError, CredentialLoginTimeoutError and CodeUrlNotAllowedError.
  • CLI 0.3.0: boxline credentials set --code-source, --code-url and --code-timeout, credentials push-code, and boxline sessions login.
  • MCP server 0.3.0: browser_login, and codeSource in credentials_list.

A new example, Email-code sign-in, in Node and Python. See Credentials.

CHANGED

Credentials and browser profiles

Changed Project secrets and a saved login’s login details are now one thing: credentials. Saved logins (contexts) are now browser profiles. This is a breaking change with no aliases: the old routes, fields and error codes are gone. Upgrade your SDK, CLI and MCP server when the versions below are out.

Credentials

  • /v1/credentials has two types. A password holds the sites it may be typed on, a user name, a password and an optional 2FA key (Hobby plan and up). A secret holds one value, such as an API token. Both are write-only, encrypted, audited and limited to 200 per project.
  • One name does three jobs. The AI types %SHOP.username%, %SHOP.password% and %SHOP.otp% (a secret is %NAME%). A shell gets $SHOP_USERNAME and $SHOP_PASSWORD (a secret is $NAME), and boxline-otp SHOP prints the current 2FA code. The 2FA key never enters the machine.
  • Both types take scope ("agent" by default, "shell" or "all") and shell. Keep website passwords at the default, so only the AI types them, and only on their own sites. Anything that runs in a shell can read what the shell exports.
  • credentials: ["NAME"] replaces secrets on sessions, exec, agent runs, steps, scripts and tasks.
  • The type action takes credential and field, so you can type a credential into a field without its value passing through your code.
  • Making a credential readable by shells later (scope to "shell" or "all", or shell: true) needs its values again in the same request, as a new site does, and two edits at once get 409 conflict. Linking a credential to a profile counts against the 60 credential writes a minute.
  • After a step or the type action has typed a credential, the session hides it in elements, content, evaluate, tabs, errors, script and exec output, and covers the field in screenshots. This hides a value; it does not protect it: code that runs in the page or the session can still read a field.
  • GET /v1/credentials/audit shows type instead of kind, with new uses action and otp. The webhook event is credential.changed.

Browser profiles

  • /v1/profiles replaces /v1/contexts, and profile: { id, persist } replaces context on sessions, agent runs and tasks. Ids start with prof_. Sessions show profileId and profilePersist.
  • PATCH /v1/profiles/:id with { credential: "SHOP" } links a password credential, so sessions that start from the profile can sign in again by themselves when the cookies expire. It replaces the login routes (/v1/contexts/:id/login) and the %login.*% placeholders.
  • Plan fields are maxProfiles and maxProfileBytes, the feature is profiles, and the error is profile_too_large. maxCredentials replaces maxSecrets.

Coming in the packages

  • Node SDK 1.2.0 and Python SDK 1.2.0: credentials.*, profiles.* (with update) and typeCredential / type_credential.
  • CLI 0.2.0: boxline credentials list, set and delete (values from a hidden prompt or stdin, never an argument), and boxline run --credential NAME.
  • MCP server 0.2.0: credentials_list, browser_type with credential and field (the model never sees the value), and session_create with profile and credentials. No tool creates a credential, so a password never passes through a chat.

Versions released before this change still call the old routes, which the API no longer answers. See Credentials and Browser profiles. The old docs addresses redirect to them.

LAUNCH

The SDKs, the MCP server, the CLI and the examples are public

The packages are on npm and PyPI, and their source is open (MIT licence) in the Boxline-dev organization on GitHub.

  • Node SDK: npm install @boxline/sdk. See npm and GitHub.
  • Python SDK: pip install boxline-sdk, imported as boxline. The name boxline on PyPI is an unrelated project, so install boxline-sdk. See PyPI and GitHub.
  • MCP server: npx -y @boxline/mcp. See npm and GitHub.
  • CLI: npm install -g @boxline/cli, or npx @boxline/cli <command>. The command is boxline: npm would not take the unscoped name. See npm and GitHub.
  • Examples: 29 new examples (research, monitoring and drafts, shopping and places, site quality, data for AI), 7 extended ones, and five Claude Code skills, all in the examples repository. Every example has a docs page and a result check. See Examples and Claude Code skills.

Keep your API key in BOXLINE_API_KEY or in the CLI’s saved login, never in code you publish. See the Node SDK, Python SDK, MCP and CLI pages.

IMPROVED

Partial extracts, PDFs, streamed commands, cleaner Markdown, and clearer failures

  • Extract with several pages. With several urls, a page that does not load is listed in pages with status: null, finalUrl: null and error: { code, message }, and the call fails only when none load. See Extract.
  • PDFs. fetch and extract (and the pages search and crawl open) answer 422 not_a_web_page for the address of a PDF, instead of an empty page. NotAWebPageError in both SDKs; it is never retried. Download the file in a session and read it there, for example with pdftotext.
  • Streamed commands. A streamed exec sends its headers at once, then {"type":"waiting"} lines while the session’s setup runs and {"type":"ping"} after 25 seconds without output. An error after the start is a {"type":"error",…} line. The Node SDK’s exec() reads the stream, so a command after a long setup no longer fails at 300 seconds, and both SDKs allow 10 minutes of setup. See Shell.
  • Markdown. From fetch, crawl and extract: no heading permalinks, and links and images are absolute addresses.
  • Page-load errors name the reason. Where Chrome only says the tunnel failed, the message adds why, for example (the name does not resolve (DNS)), for fetch, screenshot, PDF, extract, crawl and a session’s goto. See Why a page did not load.
  • Recordings keep a frame when a page finishes loading, after each agent step and when the session is released, so short visits and last screens show. A session without a browser has no recording and no browser events. See Recordings.
  • Phone-width viewports. A session viewport under 500 pixels wide (375, 390, 414, …) now has exactly that width; it used to become 500.
  • Network events carry data.bytes: the bytes transferred, compressed, headers included.
  • Agent steps name what they acted on and where the page is after it, as in Clicked button "Log in". Page: Dashboard — https://…. See What a step’s output says.
  • Fewer 503 no_capacity answers right after quick API calls. A fetch, extract or crawl at the machine limit now waits for its own busy browsers, and hands idle ones back, so a session is not refused for up to a minute.
  • CLI: boxline search "<query>" prints titles, addresses and snippets, and with --fetch n the top pages as Markdown. See the CLI.
NEW

Your own model keys, a project default model, Grok and Gemini

  • Own keys. Save your own Claude, OpenAI, Grok or Gemini key in the console (Settings, then Models) or with PUT /v1/project/model-keys/:provider. The key is checked with the provider, stored encrypted and never shown again. Per provider, use is "project" or "platform". A rejected key fails the run with model_key_rejected; there is no silent switch to Boxline’s key.
  • Billing. A run on your key has no model charge from us and is outside the plan’s model spend cap. Tokens and a list-price estimate are still shown (keySource, ownKeyModelCostUsd), and maxCostUsd still applies.
  • Default model. defaultModel in the project settings is used when a request names no model.
  • Grok and Gemini. New providers, in tools mode only.
  • Free plan. Free runs AI on your own model keys only; paid plans include Boxline’s keys, or use your own. Without a key, Free gets 402 feature_not_in_plan. New error codes: invalid_model_key, model_key_rejected, model_error. See Your own model keys.
NEW

Continue a run, message a working run, and clearer run limits

  • Continue. A run that stopped at a limit, or because its server restarted, shows continuable: { until }. POST /v1/agent/runs/:id/continue starts a new run in the same session that picks up where it stopped, with continuedFrom and continuedBy linking the two. Its own session waits up to 10 minutes for this, and that time is billed. Variables are sent again, because their values are never stored.
  • Messages. POST /v1/agent/runs/:id/messages tells a working agent something without taking the browser. It reads it at its next step. A message also answers the agent’s request for help, but not a takeover, CAPTCHA or safety-check pause. Up to 2,000 characters and 50 per run.
  • Limits. maxSteps takes 1 to 1,000, or null for no step limit. New: maxCostUsd and maxConsecutiveErrors, and a guard that stops a run repeating the same call 5 times. Give a run without a step limit a cost limit or a sensible timeout.
  • Why it stopped. errorCode is one of max_steps, max_cost, too_many_errors, no_progress, session_timeout, session_ended or server_restarted.
  • Crashed and hung tabs are replaced by a fresh tab on the same address, and the agent goes on. Actions on such a tab answer tab_crashed or tab_hung.
  • In the SDKs: agent.continueRun / continue_run and agent.sendMessage / send_message. In the CLI: boxline continue, boxline message, and --steps, --max-cost and --timeout on boxline run.

See Resume a run and Messages.

NEW

How runs and sessions end, idle timeouts, and sessions that survive API restarts

  • A new page, How runs and sessions end, puts every limit and end reason in one place, with best practices: release in finally, choose timeouts, and cancel runs you stop waiting for.
  • idleTimeout on a session ends it after that many seconds without activity, with end reason idle. Use it on sessions with keepAlive or a shell, so a crashed client does not leave a session running, and billed, until its timeout.
  • Sessions now survive a restart of the API on the hosted platform. api_restart means a session that could not be taken back.
  • Agent runs show sessionExpiresAt, and sessions show contextPersist: whether their sign-ins are saved back to their saved login.
NEW

Save a sign-in from a running session

POST /v1/contexts with fromSession saves a running session’s sign-ins into a new saved login. Sign in by hand in the live view, then save it. With attach: true the session keeps saving to it. A saved login is at most 16 MB (413 context_too_large). Sessions that save to their saved login (persist: true) now also save at checkpoints, not only when they end. A saved login signs anyone with your API key in to that account: keep one per account, and delete it when you no longer need it. See Browser profiles.

NEW

22 webhook events, typed payloads, and test deliveries of any type

  • Webhooks now cover sessions, agent runs (started, waiting, resumed, finished), CAPTCHAs, tasks, crawls, usage limits, API keys, secrets, webhook endpoints and extensions. Each event on the Webhooks page has a sample payload.
  • events: ["*"] subscribes to every type, including ones added later. GET /v1/webhooks/events lists them. Ignore types and fields your receiver does not know.
  • POST /v1/webhooks/:id/test takes {"type": "…"} and sends a sample of that type, marked test: true.
  • The SDKs type every payload: switch on type with WebhookEventPayload.
  • The security events (api_key.created, api_key.revoked, secret.changed, webhook.changed) carry names and who acted, never a key, a secret value or an endpoint URL. Agent runs list their variableNames, never values.
NEW

34 tested examples, and integration guides with tested code

  • Every example has a page: what it does and when to use it, its Node and Python code as it is in its folder, how to run it, and what its result check proves. They cover data out, logins and 2FA, forms, monitoring with scheduled tasks, AI agents, the browser with the shell, quality checks, proxies and CAPTCHAs.
  • The Playwright, Puppeteer, Stagehand, Browser Use, LangChain, CrewAI and Vercel AI SDK guides now use the tested code, with the package versions it ran with. New: OpenAI computer use and Claude computer use.
  • Stagehand: use 3.7.x with zod 3. Stagehand 4 cannot connect to a remote browser, and zod 4 makes act() send a schema OpenAI rejects.

See All examples.

NEW

Secrets, environment variables, login details with 2FA, a better shell, and data retention

Project secrets

  • POST /v1/secrets stores a value once, write-only and encrypted: no call ever returns it. On every plan, 200 per project.
  • Agent runs, plain-English steps and scripts take secrets: ["NAME"] and type %NAME% without the model seeing it, only on the secret’s own sites with origins.
  • Sessions and exec take secrets too, and export them as $NAME into the shell (scope "shell" or "all", opt-in). Exec output, events and the terminal show %NAME% instead of the value.
  • GET /v1/secrets/audit records every change and use, never a value. Changes by Boxline support show as support.
  • In the SDKs: bx.secrets, contexts.setLogin, and env and secrets on sessions.create and exec. In the CLI: boxline secrets list, set and delete, which reads the value from a hidden prompt or stdin, never an argument.
  • Tasks take secrets (project secrets by name), and scheduled tasks may use them: the way to sign in on a schedule. PATCH /v1/contexts/:id/login changes some login details and keeps the rest; a new site needs its password again. The audit log names every kind of use (task_run included) and what each change did.

See Credentials.

Environment variables

env on POST /v1/sessions sets variables for the whole shell, kept encrypted and set again on every new machine. cwd, env and secrets on exec now apply to that command only. See Environment variables.

Saved login details and 2FA

PUT /v1/contexts/:id/login keeps a site’s user name, password and 2FA setup key on a saved login. The AI types them as %login.username%, %login.password% and %login.otp% (a fresh code), only on that site, and never sees them. Hobby plan and up. See Browser profiles.

The shell

  • A time limit no longer kills background jobs or forgets the shell’s directory and exports, and exit or set -e keep them too.
  • Big output keeps its start and its end, with a [… N characters not shown …] marker, and streaming sends everything.
  • A move or pause carries each shell’s directory and exported variables, and lists the processes it stopped.

See Shell best practices.

Data retention

An ended session’s recording, logs and agent-run steps are deleted after 7 days on Free, 30 on Hobby and Startup, and 90 on Scale (retentionDays). The session stays, with its usage, and gets dataDeletedAt. See Recordings.

Changed POST /v1/auth/signup needs acceptTerms: true (400 terms_not_accepted without it), and takes an optional name.

Changed plan_limit is always 402 now: project secrets beyond maxSecrets and a session longer than the plan’s maxTimeoutSeconds used to answer 403.

Password resets and account recovery

The console can send a password reset link. Resetting an account whose email was not confirmed yet also recovers it: its API keys are revoked, its webhooks and task schedules are switched off, and its sessions stop, with the new reason account_recovered. See Password resets and account recovery.

NEW

Tasks, schedules and structured output

Structured output

Agent runs take output, a JSON Schema. The run’s result is then JSON that matches it, with resultText as a one-sentence summary. An answer that does not match gets one repair try; after that the run fails with errorCode: "output_invalid". See Structured output.

Tasks

  • POST /v1/tasks saves an agent run to use again: an instruction with %name% variables, an output schema, the session’s settings, a saved login and a model.
  • POST /v1/tasks/:id/runs runs it with new values, and GET /v1/tasks/:id/runs is its history. Secret variables are never stored and must come with every run.
  • The task_run.finished webhook sends each run’s result. agent_run.finished now carries resultText, structured, errorCode, taskId and taskRunId too.

Schedules

  • Put a task on a cron schedule in any time zone, at most every 5 minutes, with clear rules for daylight saving.
  • A task never runs twice at once: a time that comes while a run is still going is skipped. Times missed during an outage are recorded, not made up.
  • Plans: Free keeps 1 task and no schedules; Hobby 5 schedules, Startup 50, Scale no limit.

See Tasks.

NEW

Ad blocking, cookie banners and Chrome extensions

  • blockAds: true refuses ad and tracker sites inside the session’s machine, on sessions, agent runs and every page API. Off by default, on every plan. The list comes from EasyList, EasyPrivacy and AdAway.
  • cookieBanners answers the consent banners of ten common platforms with “Reject all”. It never accepts.
  • Upload a Manifest V3 extension with POST /v1/extensions and start sessions with extensions: ["ext_…"] (Hobby plan and up). Agent runs with variables in a session with extensions need allowWithExtensions: true.

Changed New sessions now reject cookie banners by default (cookieBanners: "reject"). Send "off" to keep the old behaviour. See Ad and cookie-banner blocking and Extensions.

NEW

Webhooks, and a project default for CAPTCHAs

Webhooks

  • Signed HTTPS callbacks when a session ends (session.ended), an agent run or a crawl finishes (agent_run.finished, crawl.finished), or a CAPTCHA waits for a person (captcha.waiting). On every plan.
  • Every delivery carries a Boxline-Signature; the SDKs check it with verifyWebhook / verify_webhook. Secrets can be rotated with a 24-hour overlap.
  • Retries with backoff for about 24 hours, a delivery log with each attempt, one-click retries and test events, and endpoints that switch off after a 410 or 3 days of failures.

See Webhooks.

The project’s CAPTCHA default

PUT /v1/project/settings sets captchaDefault: what new sessions and agent runs do about CAPTCHAs when they do not choose (“Solve CAPTCHAs automatically” in the console). Crawls and the page APIs are not affected. See Project settings.

NEW

Computer use, mouse and keyboard, web search, and a command line

Computer use

  • POST /v1/sessions/:id/computer runs one action exactly as Claude’s or OpenAI’s computer-use tool gives it, on the session’s browser page, and returns the screen. See Computer use, with a loop for each provider.
  • Agent runs take mode: "computer": the model drives the page with its own computer-use tool on screenshots.

Mouse and keyboard

  • New actions: move (with steps), hover, mouse_down, mouse_up, drag (between points or elements, or along a path), key (combinations such as Control+A) and cursor. click takes button, count and modifiers; scroll takes a point; screenshots take maxWidth and cursor. See Mouse and keyboard.
  • A plain string in a list of actions is a plain-English step, and every result has a text line saying what happened. See Plain-English and exact steps together.
  • Agent steps have a thought: one sentence on what the agent is doing and what comes next, also sent live as a thought event. New agent tools: double and right click, hover, move, drag, key combinations, file uploads and web_search.

POST /v1/search returns web results, and with fetch the top pages as Markdown. Every plan includes searches each month. The queries go to Brave, a search provider. See Search.

The command line

The boxline command line (in private beta then, on npm as @boxline/cli since 2 October): boxline run "…" gives the agent a task and shows it working, and other commands turn pages into Markdown, screenshots and PDFs, and manage sessions, shells and files. See the CLI.

In the SDKs and the MCP server

  • bx.search(), bx.sessions.computer(), session.mouse and session.keyboard helpers, mode on agent runs, and thought on steps. New errors: SearchUnavailableError, ModelRefusedError and OutOfViewportError.
  • Changed Without BOXLINE_API_URL, the SDKs now use the public API, like the CLI, instead of http://localhost:8080. The Python package is boxline-sdk on PyPI, still imported as boxline.
  • The MCP server has new tools: mouse_move, mouse_click, mouse_drag, hover, key, computer and web_search.
IMPROVED

SDKs 1.0: safe retries, idempotency keys, typed errors and list iterators

The Node and Python SDKs (public on npm and PyPI since 2 October) now have a method for every endpoint, and the docs show them on every API reference page.

  • Methods on one session take its id first, such as bx.sessions.pause(id); a session object has the same methods without it.
  • Reads and creates are retried after network errors, 429 and 5xx, waiting as long as Retry-After says. Creates send an Idempotency-Key by themselves, so a retry never starts a second session or run. Other calls are never retried.
  • Errors are typed (RateLimitError, NotFoundError…) and carry the API’s requestId and your clientRequestId.
  • Lists return their first page; iterate them to get every item. Python has an async client, AsyncBoxline.

Changed A 429 concurrency_limit now says Retry-After: 5, and a 409 idempotency_in_progress says Retry-After: 1.

Every AI task and script in the Examples now comes with the API call that runs it, in curl, Node and Python, and a link that opens it in the playground. See the Node SDK and Python SDK pages.

NEW

Request ids, rate-limit headers, safe retries, cursor pages, agent variables, and new docs

API basics

  • Every response has an X-Request-Id, and every error body has it as error.requestId. Send X-Client-Request-Id to tie a request to your own logs; it comes back in the same header. See Errors and request ids.
  • Limited routes send RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset. Bulk session actions are limited to 30 per minute. See Rate limits.
  • Idempotency-Key makes retries of POST /v1/sessions, /v1/sessions/bulk, /v1/agent/runs, /v1/crawl, /v1/api-keys and /v1/contexts safe. See Idempotency keys.
  • Every list takes limit and after and returns { data, next }. offset still works. See Pagination.
  • Changed next on GET /v1/crawl/:id is now a string. Pass it back as after; sending it as offset still works.
  • GET /v1/openapi.json describes the whole API in OpenAPI 3.1, with no API key needed.

Agent variables and script models

  • Agent runs take variables: values the agent types as %name% without the model ever seeing them, with origins to limit a password to its site and shell to let commands use a token. See Variables.
  • A run’s steps now say what happened to a CAPTCHA it paused for (captcha steps). See CAPTCHA steps.
  • Scripts take ai, and useModel() picks the model for step() and extract() from the code. See Scripts.

New docs

NEW

Automatic CAPTCHA solving, plans with features, and the Trajectories setting

Automatic CAPTCHA solving (opt-in, paid plans)

The default does not change: a person answers. A session can now opt in to having the platform try first. Read Automatic solving for the details.

  • New captcha: "solve" on POST /v1/sessions, PATCH /v1/sessions/:id and POST /v1/agent/runs. It tries reCAPTCHA v2, hCaptcha and Cloudflare Turnstile, and nobody is asked while it does.
  • When it cannot or may not solve one, it asks a person exactly like "ask": for other kinds of CAPTCHA, on government and military sites, when the plan’s monthly allowance is used up, when solving is switched off, or when the solving service fails.
  • attention has state ("solving" or "waiting") and reason. New captcha event states: solving, solved, solve_failed and solve_refused (with data.reason).
  • Attempts that reach the solving service are billed per 1,000 (captchaSolving in GET /v1/pricing), up to your plan’s limits.captchaSolvesPerMonth. GET /v1/usage reports them as captchaSolves.
  • Only for sites you are allowed to automate. See the acceptable-use rules in the Terms of Service.

Plans and features

Read Plans and features for the details.

  • Plans now have feature switches as well as limits. GET /v1/auth/me returns them as project.limits.features, and GET /v1/pricing lists the public plans (plans) and each feature’s label (features).
  • A call that needs a feature your plan does not include gets 402 feature_not_in_plan. By default, the platform’s residential and datacenter proxies, browser mode and automatic CAPTCHA solving are for paid plans. Your own proxy works on every plan.
  • Changed On the Free plan, sessions with browser mode ("realistic", locale or timezone) now get 402 feature_not_in_plan, and asking for the platform’s proxies gets feature_not_in_plan instead of plan_limit.
  • A suspended project gets 403 project_suspended on every call except GET /v1/auth/me and signing in and out. project.suspended in GET /v1/auth/me says since when and why.
  • SDKs: client.me(), client.hasFeature() and the PlanFeature and PlanLimits types (TypeScript); bx.me() (Python).

The Trajectories program setting

Read Trajectories program for the details, and the Terms of Service and Privacy Policy for what it covers.

  • A project setting, on by default. The console asks at first use, and you can turn it off at any time.
  • Only sessions started while it is on, and after your project was shown the notice, are eligible. Sessions show trajectoriesEligible, fixed when they start.
  • GET and PUT /v1/project/trajectories; GET /v1/auth/me returns it as project.trajectories.
  • SDKs: client.project.trajectories() and setTrajectories() (TypeScript), bx.project.trajectories() and set_trajectories() (Python).
NEW

Browser mode: a clock and language that match the proxy

A session leaving from Berlin can now also say it is in Berlin. Read Browser mode for the details.

  • browser on POST /v1/sessions still takes true or false, and now also { "mode": "standard" | "realistic", "locale": "de-DE", "timezone": "Europe/Berlin" }. An object means the session gets a browser.
  • "realistic" puts the browser’s clock, language and Accept-Language on the proxy’s country, narrowed by its US state or city. Without a managed proxy, or for a country we have no entry for, nothing changes.
  • locale (BCP 47) and timezone (IANA) win over the mode; a value the browser would not accept is 400 invalid_request.
  • Sessions show browserSettings: { mode, locale, timezone }, what is actually in force. It is applied to every tab, and again after a move, resume or recovery.
  • PATCH /v1/sessions/:id takes browser to change it while the session runs; open tabs follow within a second or two. In realistic mode, changing the proxy moves the clock and language with it.
  • POST /v1/fetch, /v1/screenshot, /v1/pdf, /v1/extract and /v1/crawl take the same browser object (a crawl applies it to every page), and POST /v1/agent/runs takes it for the run’s own session.
  • SDKs: BrowserOptions on sessions.create, session.update, agent.run and the rendering APIs (TypeScript); browser_options= on sessions.create and browser= on the quick APIs, session.update and agent.run (Python).
  • This is not fingerprint spoofing, and it does not hide that the session is automated. Boxline does not offer fingerprint spoofing.
IMPROVED

Sessions keep their page size

  • Sessions return viewport: { width, height } of the page (default 1280×720, null without a browser). See the session object.
  • The size you ask for is now kept across moves, resumes and recoveries; before, those fell back to the default.
  • viewport is the size of the page itself, not of the browser window around it.
NEW

CAPTCHAs: we tell you, and wait for a person

When a page in a session shows a CAPTCHA that needs a person, Boxline now notices it and hands over to a person. By default it does not solve them (see automatic solving). Read CAPTCHAs for the details.

  • Recognised: reCAPTCHA, hCaptcha, Cloudflare Turnstile and the “Just a moment…” page, DataDome, Arkose and HUMAN press-and-hold. Only visible, unanswered challenges count; invisible widgets and checks that pass by themselves are not reported.
  • Sessions have attention (the CAPTCHA waiting, or null), and log captcha events when one is detected and when it is cleared.
  • New captcha option on POST /v1/sessions and PATCH /v1/sessions/:id: "ask" (default) or "ignore".
  • Agent runs pause with handover.by: "captcha" and continue by themselves once a person has solved it in the live view; they fail after 5 minutes if nobody does. Plain-English steps wait up to 4 minutes, then fail with captcha_timeout. Failed action results now carry a code when there is one.
  • Fetch returns captcha, screenshot and PDF send an x-page-captcha header, and crawl pages have captcha.
  • SDKs: session.waitForHuman(), session.onCaptcha() and captcha on crawl pages (TypeScript), session.wait_for_human() and captcha= on sessions.create and session.update (Python).
NEW

A different proxy for each site, and proxy: true

Read Different proxies for different sites for the details.

  • proxy can be a list of up to 10 rules. Each rule is a proxy, or { "type": "none" } to go straight out, with an optional domainPattern (a regular expression on the site’s host name). The first match wins; no match goes straight out.
  • Each proxy in a list keeps its own sticky IP; POST /v1/sessions/:id/proxy/rotate gives them all new ones.
  • proxy: true is short for a residential proxy in the US.
  • Agent runs take proxy for the session they create.
  • Lists work on new sessions, PATCH /v1/sessions/:id, agent runs, fetch, screenshot, PDF, extract and crawl.
  • New error reason bad_route in the X-Boxline-Proxy-Error header.
  • SDKs: TypeScript ProxyOption is true, one proxy or a list of ProxyRule; Python’s proxy= takes True, a dict or a list of dicts.
NEW

Proxies: residential, datacenter and your own, per session and per request

Sessions can now send their traffic through a proxy. Read the Proxies guide for the details.

  • proxy on POST /v1/sessions: residential (by country, US state or city), datacenter (by country), or your own http:// or https:// proxy. Its password is stored encrypted and never returned.
  • Sticky IPs by default for sessions and crawls, and a new IP on demand with POST /v1/sessions/:id/proxy/rotate.
  • Change or remove the proxy of a running session with PATCH /v1/sessions/:id.
  • scope: "all" sends the shell’s traffic (curl, pip, git) through the proxy too.
  • The same proxy option on fetch, screenshot, PDF, extract (rotating IPs by default) and crawl (one IP per crawl).
  • Pause, resume and move keep the proxy, and the IP while the provider still has it.
  • Proxy data is billed per GB, with a monthly allowance per plan; your own proxy has no data charge. GET /v1/usage reports proxy data.
  • SDKs: setProxy and rotateProxy (TypeScript), set_proxy and rotate_proxy (Python). The MCP server’s session_create takes proxyType, proxyCountry and proxyCity.
LAUNCH

Private beta

Boxline is open in private beta. Read the announcement for the background. Available today:

  • Sessions with a Chrome browser (Playwright, Puppeteer or raw CDP) and a live view.
  • Optional bash shell with Python and Node included, persistent sessions, streaming exec and an interactive terminal.
  • Shared /workspace disk with read, write, list, delete and wait-for-file endpoints; browser downloads land in /workspace/downloads.
  • Warm-pool starts, session move, and pause and resume.
  • Contexts for keeping logins between sessions, and cookie export for reusing a browser login from the shell.
  • Actions API, Fetch API and Agent API.
  • MCP server and TypeScript SDK.
  • Per-second billing with Free, Hobby, Startup and Scale plans.

Coming soon: residential proxies, desktop (computer-use) sessions and GPU sessions.