Docs

Browser

Every sandbox has a browser in it that shows as a browser on an ordinary computer does, and the command-line tools agents drive it with. Your own code can drive it from outside as well.

For your agent

agental.io/docs/browser.md

There is nothing to install. The browser is run by commands, like anything else in the sandbox, with no call to turn on. A program on your own computer can drive it too, once it is turned on for that, as in Drive it from your own computer. A sandbox made before the browser was added does not have it: create a new one.

Drive it with playwright-cli

playwright-cli is Playwright's command-line tool. It keeps one browser open between commands, so each command is short, and it writes what it reads to a file instead of printing a whole page.

shell
playwright-cli open https://example.com
playwright-cli snapshot
playwright-cli click e6
playwright-cli screenshot
playwright-cli close
  • snapshot writes the page as a list of what is on it, and says where. Each thing that can be used has a reference, such as e6, which click, fill and type take.
  • It drives the Chrome in the sandbox, with no window.
  • With --headed it opens a window on the desktop, when that is on.
  • playwright-cli --help lists every command.

Send the commands as you send any other, one request each or several in one line of shell:

curl -X POST https://agental.io/v1/sandboxes/{id}/exec \
  -H "Authorization: Bearer $AGENTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"command":["sh","-c","playwright-cli open https://example.com && playwright-cli snapshot"]}'

Chrome by itself

For one picture, one PDF, or a page as it is once its scripts have run, Chrome alone does it:

shell
google-chrome --headless --screenshot=/opt/agental/page.png --window-size=1280,800 https://example.com
google-chrome --headless --print-to-pdf=/opt/agental/page.pdf https://example.com
google-chrome --headless --dump-dom https://example.com
  • A file written under /opt/agental is read back with the files call.
  • Commands run as root, and Chrome will not start as root with its own sandbox on. google-chrome and playwright-cli here start it without, since the machine is yours alone.
  • A library that starts Chrome itself, such as Puppeteer, has to be told so: pass --no-sandbox.
  • Playwright finds this Chrome as the channel chrome, so no other browser has to be downloaded.
  • Anything that speaks the DevTools protocol can drive it: start Chrome with --remote-debugging-port=9222 and connect to that port. While the browser is turned on as below, that port is its own, and the Chrome there is the one to connect to.

What is in the image

WhatWhich
BrowserBetterChromium, the Chromium of the BetterWright project, in Chrome's place: google-chrome, and Playwright's channel chrome, start it
BetterWrightbetterwright, BetterWright's own tool for the same browser
Driverplaywright-cli, from the npm package @playwright/cli
RuntimeNode.js 24, with npm
FontsLiberation, Noto, Noto CJK and Noto Color Emoji

Drive it from your own computer

Playwright, Puppeteer, browser-use, Stagehand and anything else that speaks the Chrome DevTools Protocol can drive the Chrome in a sandbox from where they run. Turned on for that, Chrome runs in the sandbox's machine with its DevTools on, and keeps running between your scripts. It uses the sandbox's own CPU and memory, and is not charged for by itself.

PUT/v1/sandboxes/{id}/browser
curl
curl -X PUT https://agental.io/v1/sandboxes/{id}/browser \
  -H "Authorization: Bearer $AGENTAL_API_KEY"
json
{
  "enabled": true,
  "enabledAt": "2026-10-14T09:02:11.480Z",
  "running": true,
  "headless": true,
  "cdpUrl": "wss://b51e09c4d7a2f8e3c610.agental.site/devtools/browser",
  "localCdpUrl": "http://127.0.0.1:9222"
}
  • The sandbox has to be running. The call answers when Chrome is up.
  • cdpUrl is where to connect. The address is hard to guess and asks for no login: whoever has it has the whole browser, with what it is signed in to. Keep it as you would a password, and do not paste it into a chat or a log.
  • It is another address each time the browser is turned on after being off. To cut off whoever has it, turn the browser off and on again.
  • Asked again, the call changes nothing that is already so, and starts a Chrome that had ended.
  • A sandbox made before this was added answers 409 browser_unavailable. Create a new one.

Connect to it

Playwright takes the address as it is:

typescript
import { chromium } from "playwright";

const browser = await chromium.connectOverCDP("wss://b51e09c4d7a2f8e3c610.agental.site/devtools/browser");
const context = browser.contexts()[0];
const page = await context.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.close();

So does Puppeteer:

javascript
import puppeteer from "puppeteer-core";

const browser = await puppeteer.connect({ browserWSEndpoint: "wss://b51e09c4d7a2f8e3c610.agental.site/devtools/browser" });
const page = await browser.newPage();
await page.goto("https://example.com");
console.log(await page.title());
await browser.disconnect();

And browser-use, where llm is one of its chat models:

python
import asyncio
from browser_use import Agent, Browser

async def main():
    browser = Browser(cdp_url="wss://b51e09c4d7a2f8e3c610.agental.site/devtools/browser")
    agent = Agent(task="Open example.com and say what it is for", llm=llm, browser=browser)
    await agent.run()

asyncio.run(main())

Stagehand takes it among its options for a local browser:

typescript
import { Stagehand } from "@browserbasehq/stagehand";

const stagehand = new Stagehand({
  env: "LOCAL",
  localBrowserLaunchOptions: { cdpUrl: "wss://b51e09c4d7a2f8e3c610.agental.site/devtools/browser" },
});
await stagehand.init();
  • A sandbox has one Chrome to drive, and many clients can be connected to it at once. Each sees the tabs the others open.
  • In Playwright, browser.contexts()[0] is Chrome's own context, with the cookies and logins in its profile. One made with browser.newContext() starts empty.
  • Playwright's browser.close() disconnects, and leaves Chrome running. Puppeteer's ends Chrome, which is started again within a second at the same address, with no tabs open. Call browser.disconnect() to leave it as it is.

A client that takes an http address instead, such as chrome-devtools-mcp, is given https:// and the host of cdpUrl, with no path:

shell
npx chrome-devtools-mcp@latest --browser-url=https://b51e09c4d7a2f8e3c610.agental.site

With no window, or on the desktop

With no body, Chrome is on the desktop when one is turned on for the sandbox, and has no window when none is. A browser that is already on keeps what it has. To say which, send headless:

curl
curl -X PUT https://agental.io/v1/sandboxes/{id}/browser \
  -H "Authorization: Bearer $AGENTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"headless":false}'
  • false puts Chrome on the desktop, where it is watched as it is driven. A desktop has to be turned on first, or the call answers 409 desktop_not_running.
  • true gives it no window.
  • Asked for the other, Chrome is started again so, which closes its tabs. cdpUrl stays the same.
  • A Chrome on the desktop waits while the desktop is off, with running false, and comes back on it when the desktop is turned on again.

The same Chrome from inside

localCdpUrl is where programs in the sandbox reach it. playwright-cli attaches to it, and its commands then drive the browser your own code drives:

shell
playwright-cli attach --cdp=http://127.0.0.1:9222
playwright-cli snapshot
playwright-cli detach
  • playwright-cli detach lets go of it, and leaves it running.
  • So a script on your computer and an agent in the sandbox can use one browser, with one profile.

After a stop

The browser stays turned on for the sandbox. Chrome ends when the sandbox stops, and is started again by itself a moment after the sandbox runs again, with no tabs open.

GET/v1/sandboxes/{id}/browser
  • It changes nothing. running is false while the sandbox is not running, and for a moment after a start. cdpUrl is null then.
  • The address is the same after a start, so a client that lost its connection connects to it again once running is true.
  • Chrome's profile is in /root/.config/agental-browser, on the sandbox's disk. Its cookies and logins are kept through a stop, and go with snapshots and forks. A fork does not have the browser turned on.

Turn it off

DELETE/v1/sandboxes/{id}/browser

Stops Chrome, which closes its tabs. Its address leads nowhere from then on, and what is connected through it is cut. The profile stays on the sandbox's disk, and is there when the browser is turned on again.

In the console

A sandbox's Browser view turns it on, with no window or on the desktop, and off. While Chrome runs it shows the address, the lines that connect Playwright, Puppeteer and browser-use to it, and the command that attaches playwright-cli in the sandbox.

Errors

AnswerMeaning
409 sandbox_not_runningThe sandbox is stopped. Start it, then repeat the request.
409 desktop_not_runningChrome was to be on the desktop, and no desktop is turned on. Turn one on, or send {"headless": true}.
409 browser_unavailableThe machine was made from an image that cannot run the browser to be driven from outside, or Chrome did not start. message says which.
501 browser_not_configuredSandboxes here have no browser to drive from outside. GET /v1/policy says so as browser.

The full machine-readable reference is OpenAPI 3.1. Every page as Markdown, for an agent: llms.txt. Questions: [email protected]