Run a Playwright script
Runs JavaScript with Node inside the session's own sandbox, next to its Chrome (the session needs a browser, else 409 browser_disabled, and a shell, else 409 shell_disabled). In scope: page, context, browser (Playwright, already connected), env and console; top-level await works; do not call browser.close(). The reply streams NDJSON exactly like a streaming exec. Never replayed by Idempotency-Key.
/v1/sessions/{id}/browser/scriptRequest
| Method and path | POST/v1/sessions/{id}/browser/script |
|---|---|
| Base URL | https://api.boxline.dev |
| Authentication | Your API key in the x-api-key header (or Authorization: Bearer). The examples read it from BOXLINE_API_KEY. |
Path parameters
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
idrequired | string | – | – |
Body
JSON with content-type: application/json. Required.
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
coderequired | string | – | – | Up to 200000 characters. |
env | object | – | – | |
timeoutMs | integer | 120000 | – | From 1000 to 600000. |
credentials | string[] | – | – | Credentials the script's step() calls may use as placeholders (scope "agent" or "all"). The values never go into the machine: step() runs on the platform, which fills them in. Hidden in the output. The grant is for this run only and ends when the script exits or the caller disconnects. A credential the session's profile links is offered only when listed here. Up to 50 items. |
allowWithExtensions | boolean | false | – | Allow credentials in a session with Chrome extensions, which can read every typed value (400 variables_with_extensions otherwise). Signed into the script's grant; logs a warning in the session's events. |
ai | object | – | – | The model step() and extract() use unless a call, or useModel(), picks its own. With only model, the provider is the one that lists it; with only provider, its fast step model. Needs the plan's steps. |
ai.provider | string | – | anthropic, openai, xai, google | Anthropic Claude, OpenAI, Grok (xAI), Google Gemini. Grok and Gemini have no computer use (mode computer is 400). |
ai.model | string | – | – |
curl -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 '{ "code": "await page.goto('\''https://example.com'\''); console.log(await page.title());" }'import { Boxline } from "@boxline/sdk";const bx = new Boxline(); // reads BOXLINE_API_KEY and BOXLINE_API_URLconst result = await bx.sessions.runScript("SESSION_ID", "await page.goto('https://example.com'); console.log(await page.title());", { onData: (_stream, text) => process.stdout.write(text),});console.log("exit code", result.exitCode);from boxline import Boxlinebx = Boxline() # reads BOXLINE_API_KEY and BOXLINE_API_URLwith bx.sessions.run_script("SESSION_ID", "await page.goto('https://example.com'); console.log(await page.title());") as script: for stream, text in script: print(text, end="")print("exit code", script.result["exitCode"])Responses
200 OK
The script's output as it happens.
application/x-ndjson: Newline-delimited JSON (one object per line), sent as it happens.
Fields
| Name | Type | Default | Allowed values | Description |
|---|---|---|---|---|
type | string | – | stdout, stderr, exit | |
data | string | – | – | |
exitCode | integer | null | – | – | |
timedOut | boolean | – | – | |
durationMs | integer | – | – | |
truncated | boolean | – | – |
{"type":"stdout","data":"Example Domain\n"}{"type":"exit","exitCode":0,"timedOut":false,"durationMs":812}Errors
Errors have a matching HTTP status and the same body. code is stable, so check it in your code; message is for people. See Errors and request ids.
| Status | Codes | When |
|---|---|---|
| 400 Bad Request | invalid_request, invalid_json, invalid_cursor, invalid_idempotency_key, invalid_extension, variables_with_extensions, terms_not_accepted, invalid_reset_token, invalid_confirmation_code, wrong_password, no_password, invalid_next, invalid_oauth_token | The request is not valid. |
| 401 Unauthorized | unauthorized | Missing or invalid API key. |
| 404 Not Found | not_found, invite_not_found, provider_unavailable | No such thing in this project (or organization), or no such route. |
| 409 Conflict | session_not_running, shell_disabled, browser_disabled, nothing_saved, not_resumable, invalid_state, conflict, email_taken, identity_in_use, provider_already_linked, last_sign_in_method, not_impersonating, machine_too_old, too_many_credential_values, limit_reached, terms_version_mismatch, no_project, last_owner, organization_not_empty, project_has_sessions, already_member, invite_pending, idempotency_in_progress | The current state does not allow it, or a request with this Idempotency-Key is still being answered. |
| 502 Bad Gateway | machine_error, machine_unreachable, page_unreachable | The session machine failed or could not be reached, or, for fetch, screenshot, pdf and extract, the page could not be loaded. |
{ "error": { "code": "not_found", "message": "session not found", "requestId": "req_4fJx0aQm2Lk9Pz7N" }}Use with the browser
A script can use the browser and the shell together: Playwright and step() for the pages, child_process and fs for the workspace. See Browser, shell and files together.
# The body is JSON: put the script (total.js, above) in "code".jq -n --rawfile code total.js '{code: $code, timeoutMs: 300000}' | curl -N -X POST https://api.boxline.dev/v1/sessions/$SESSION_ID/browser/script \ -H "x-api-key: $BOXLINE_API_KEY" \ -H "content-type: application/json" \ -d @-const code = `const { execSync } = require("child_process");await page.goto("https://books.toscrape.com");await step("click the first book");return execSync("python3 -c 'print(6*7)'").toString().trim();`;const result = await session.runScript(code, { onData: (_s, text) => process.stdout.write(text) });code = """const { execSync } = require("child_process");await page.goto("https://books.toscrape.com");await step("click the first book");return execSync("python3 -c 'print(6*7)'").toString().trim();"""with session.run_script(code) as script: for stream, text in script: print(text, end="")Learn more
These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.