Mental model
Four objects, one visible browser
WebBrain separates the browser you rent from the work you ask it to do. Create a browser session once, send it many runs, and turn a successful run into a reusable workflow when the steps should repeat exactly.
browser_sessionThe remote Chrome browserOwns its profile, lifecycle, proxy state, downloads, and live noVNC view.
runtime_readyThe safe-to-run signalWait for this boolean before dispatching work. A created Droplet is not yet a connected browser.
runOne automation turnContains a task, its status, updates, final result, and optional request for user input.
workflowA replayable successCompiles a completed trace into a sanitized definition with validated parameters.
Access
Authenticate every request
API keys belong to one account and can control only that account's browsers, runs, workflows, and files. The complete key is displayed once when it is created.
- Create a keyOpen the dashboard's API Keys panel and copy the new wbp_… secret.
- Store it outside codeSet WEBBRAIN_API_KEY in your environment or secret manager.
- Send a Bearer headerInclude Authorization: Bearer … on every API call.
Lifecycle
Choose the browser that fits the job
A browser session is a private Chrome environment with its own profile, network state, files, and automation bridge. Both browser types remain until you explicitly destroy them.
Keeps Chrome on a private profile volume. Pause it to stop the Droplet, then resume later with logins and settings preserved.
Keeps Chrome and Downloads on one running Droplet. Pause is unavailable; destroy it when the work is finished.
POST /api/browser-sessions request body
An empty JSON object is valid and creates a normal browser. Infrastructure placement, Droplet size, proxy credentials, and the reserved WebBrain Cloud connection are private server configuration and cannot be overridden by this request.
| Field | Type | Default | Applies to | Purpose |
|---|---|---|---|---|
display_name | string | None | Both | An optional dashboard label up to 120 characters. |
type | string enum | normal | Both | normal or incognito, matching the dashboard. |
proxy_enabled | boolean | Server default | Both | true uses the server-configured proxy; false uses a direct connection. |
proxy_country | string | rotate | Both | Optional country or region code (e.g. us, de) passed when proxy_enabled is true. Substituted into % template placeholders in server proxy URLs, falling back to rotate if omitted or unconfigured. |
webbrain_config | webbrain-config/1 object | None | Both | Optional sparse Settings import copied directly from WebBrain's /export --config output. |
DigitalOcean region and size come only from DO_REGION and DO_SIZE. Proxy routing and credentials come only from the server's proxy environment. They are intentionally absent from the public request body.
Import WebBrain Settings at creation
Paste the complete JSON produced by WebBrain's /export --config command into webbrain_config. The export metadata is accepted unchanged. Settings are sparse: omitted fields keep their normal WebBrain or cloud defaults, accepted fields are applied before runtime_ready, and invalid or managed fields are ignored without failing browser creation.
Editable fields are wbLocale, themeMode, verboseMode, selectionShortcutEnabled, helpImproveWebBrain, voiceInputEnabled, notifySound, completionConfetti, providerFilter, screenshotFallback, autoScreenshot, useSiteAdapters, apiMutationObserverEnabled, screenshotRedaction, the agent/cost limits, captchaSolverEnabled, capsolverApiKey, providers, and activeProvider.
WebBrain Cloud connection fields, plan/review policy, tracing, permissions, Downloads path, local-network access, schedules, profile, memory, and custom skills remain platform-managed. Additional providers are merged, but private/local endpoints are ignored and the reserved webbrain_cloud connection always keeps its platform-issued URL, model, and session token. A valid configured external provider may be selected with activeProvider.
Config exports contain plaintext provider and CapSolver API keys. Send them only over HTTPS, never log the request body, and remember that normal browsers retain accepted settings on their profile volume while incognito settings disappear when the browser is destroyed.
export WEBBRAIN_API_KEY='wbp_your_key_here'
curl --fail-with-body -sS -X POST \
https://webbrain.cloud/api/browser-sessions \
-H "Authorization: Bearer $WEBBRAIN_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @- <<'JSON' | jq '.webbrain_config_result'
{
"type": "incognito",
"webbrain_config": {
"schema": "webbrain-config/1",
"settings": {
"captchaSolverEnabled": true,
"capsolverApiKey": "replace-with-your-key",
"activeProvider": "webbrain_cloud"
}
}
}
JSON
import { WebBrainClient } from './clients/node/webbrain-client.js';
const client = new WebBrainClient({
apiKey: process.env.WEBBRAIN_API_KEY,
});
// Replace this object with WebBrain's /export --config output.
const webbrainConfig = {
schema: 'webbrain-config/1',
settings: {
captchaSolverEnabled: true,
capsolverApiKey: 'replace-with-your-key',
activeProvider: 'webbrain_cloud',
},
};
const session = await client.createBrowserSession({
type: 'incognito',
webbrain_config: webbrainConfig,
});
console.log(session.webbrain_config_result);
import os
from clients.python.webbrain_client import WebBrainClient
client = WebBrainClient(os.environ["WEBBRAIN_API_KEY"])
# Replace this object with WebBrain's /export --config output.
webbrain_config = {
"schema": "webbrain-config/1",
"settings": {
"captchaSolverEnabled": True,
"capsolverApiKey": "replace-with-your-key",
"activeProvider": "webbrain_cloud",
},
}
session = client.create_browser_session(
type="incognito",
webbrain_config=webbrain_config,
)
print(session["webbrain_config_result"])
<?php
require_once __DIR__ . '/clients/php/WebBrainClient.php';
$client = new WebBrainClient(getenv('WEBBRAIN_API_KEY') ?: '');
// Replace this whole NOWDOC body with WebBrain's /export --config output.
$webbrainConfigJson = <<<'JSON'
{
"schema": "webbrain-config/1",
"exportedAt": "2026-07-19T10:00:00.000Z",
"webbrainVersion": "7.3.0",
"warning": "Contains plaintext provider API keys and other sensitive Settings data. Store securely.",
"settings": {
"captchaSolverEnabled": true,
"capsolverApiKey": "replace-with-your-key",
"activeProvider": "webbrain_cloud"
}
}
JSON;
$session = $client->createBrowserSession([
'type' => 'incognito',
'webbrain_config' => json_decode(
$webbrainConfigJson,
true,
512,
JSON_THROW_ON_ERROR,
),
]);
print_r($session['webbrain_config_result'] ?? []);
Config import result
When webbrain_config is supplied, the create response includes accepted field paths, ignored fields with stable reason codes, non-secret warnings, and the security settings actually enforced by the managed runtime. Values and credentials are never echoed.
{
"browser_session": {
"id": "bs_example",
"status": "provisioning"
},
"webbrain_config_result": {
"accepted": [
"settings.captchaSolverEnabled",
"settings.capsolverApiKey",
"settings.activeProvider"
],
"ignored": [
{
"field": "settings.planBeforeActMode",
"reason": "platform_managed"
}
],
"warnings": [],
"enforced": {
"strictSecretMode": true,
"scheduledRequireConsequentialConfirmation": false
}
}
}
/api/browser-sessionsCreate a normal or incognito browser./api/browser-sessionsList your sessions./api/browser-sessions/:sessionIdRead readiness./api/browser-sessions/:sessionIdSet its display name./api/browser-sessions/:sessionId/proxyRead proxy and exit IP./api/browser-sessions/:sessionId/proxyEnable or disable the server-configured proxy without restart./api/browser-sessions/:sessionId/proxyReturn to a direct connection./api/browser-sessions/:sessionId/resetRestart the running browser./api/browser-sessions/:sessionId/pauseStop the Droplet and retain the profile./api/browser-sessions/:sessionId/resumeAttach the profile to a new Droplet./api/browser-sessions/:sessionIdDestroy the browser and its infrastructure./api/browser-sessions/:sessionId/connect-tokenCreate a noVNC link./api/browser-sessions/:sessionId/downloads-accessCreate private Downloads credentials.Copy and adapt
Create and run in your language
These examples create a named normal browser with a direct connection, wait until its runtime is ready, run one visible task, and print the result. The bundled clients have no third-party runtime dependencies.
import { WebBrainClient } from './clients/node/webbrain-client.js';
const client = new WebBrainClient({
apiKey: process.env.WEBBRAIN_API_KEY,
});
const session = await client.createBrowserSession({
display_name: 'Research browser',
type: 'normal',
proxy_enabled: false,
});
const ready = await client.waitForBrowserSession(session.id);
const run = await client.createRun(ready.id, {
task: 'Open example.com and return the page title',
});
const finished = await client.waitForRun(ready.id, run.run_id);
console.log(finished.result);
import os
from clients.python.webbrain_client import WebBrainClient
client = WebBrainClient(os.environ["WEBBRAIN_API_KEY"])
session = client.create_browser_session(
display_name="Research browser",
type="normal",
proxy_enabled=False,
)
ready = client.wait_for_browser_session(session["id"])
run = client.create_run(
ready["id"],
"Open example.com and return the page title",
)
finished = client.wait_for_run(ready["id"], run["run_id"])
print(finished["result"])
<?php
require_once __DIR__ . '/clients/php/WebBrainClient.php';
$client = new WebBrainClient(getenv('WEBBRAIN_API_KEY') ?: '');
$session = $client->createBrowserSession([
'display_name' => 'Research browser',
'type' => 'normal',
'proxy_enabled' => false,
]);
$ready = $client->waitForBrowserSession($session['id']);
$run = $client->createRun(
$ready['id'],
'Open example.com and return the page title',
);
$finished = $client->waitForRun($ready['id'], $run['run_id']);
print_r($finished['result']);
File transfer
Upload and download files
For a ready or paused browser, request access metadata from POST /api/browser-sessions/:sessionId/downloads-access. The response contains an HTTPS url, username, password, upload_limit_bytes, and expires_at. It is returned with Cache-Control: no-store; do not log it.
# Obtain access and keep the secret out of the literal shell history
DOWNLOADS_ACCESS=$(curl --fail-with-body -sS -X POST "https://webbrain.cloud/api/browser-sessions/$SESSION_ID/downloads-access" -H "Authorization: Bearer $WEBBRAIN_API_KEY" -H "Content-Type: application/json" -d '{}')
DOWNLOADS_URL=$(printf '%s' "$DOWNLOADS_ACCESS" | jq -r '.url')
DOWNLOADS_USER=$(printf '%s' "$DOWNLOADS_ACCESS" | jq -r '.username')
DOWNLOADS_PASSWORD=$(printf '%s' "$DOWNLOADS_ACCESS" | jq -r '.password')
# Machine-readable listing
curl --fail-with-body -sS -u "$DOWNLOADS_USER:$DOWNLOADS_PASSWORD" -H 'Accept: application/json' "$DOWNLOADS_URL" | jq
# Browser-local streaming upload; response.browser_path is immediately usable
LOCAL_FILE='./report.pdf'
REMOTE_NAME=$(jq -rn --arg name "$(basename -- "$LOCAL_FILE")" '$name | @uri')
curl --fail-with-body -sS -X PUT -u "$DOWNLOADS_USER:$DOWNLOADS_PASSWORD" -H 'Content-Type: application/octet-stream' -H 'X-WebBrain-Upload-Target: browser' --upload-file "$LOCAL_FILE" "${DOWNLOADS_URL}${REMOTE_NAME}" | jq
# Full download and a byte-range download
curl --fail-with-body -sS -u "$DOWNLOADS_USER:$DOWNLOADS_PASSWORD" --output './report.pdf' "${DOWNLOADS_URL}${REMOTE_NAME}"
curl --fail-with-body -sS -u "$DOWNLOADS_USER:$DOWNLOADS_PASSWORD" -H 'Range: bytes=0-1023' --output './report.first-1KiB' "${DOWNLOADS_URL}${REMOTE_NAME}"
Upload response
A successful PUT returns the final collision-safe filename, byte size, SHA-256 digest, storage backend, and whether the file is already available at an absolute path inside the cloud browser.
{
"name": "github-avatar-test.jpg",
"size": 38564,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",
"storage_backend": "browser_local",
"browser_path": "/root/Downloads/github-avatar-test.jpg",
"browser_ready": true
}
| Field | Type | Meaning |
|---|---|---|
name | string | Final filename after any collision suffix is applied. |
size | number | Stored byte count. |
sha256 | string | Lowercase SHA-256 hex digest of the uploaded content. |
storage_backend | string | browser_local or shared_object. |
browser_path | string or null | Absolute browser-visible filesystem path when locally materialized; otherwise null. |
browser_ready | boolean | true only when the browser can open browser_path immediately. |
Incognito uploads are browser-local. For a ready, running normal browser, send X-WebBrain-Upload-Target: browser on the PUT to upload directly to that browser and receive its real absolute Downloads path with browser_ready: true. The request returns 409 Conflict while the browser is paused or not ready. Without the header, normal uploads use shared storage, remain available while paused, and return storage_backend: "shared_object", browser_path: null, and browser_ready: false. Existing path, url, etag, and idempotent fields may also be present for compatibility and shared-storage bookkeeping.
For normal browsers, shared Downloads are scoped to the user, remain online while Droplets are paused, and have a default 25 GiB fair-use allowance per user. Directory requests return the file tray by default and JSON when sent Accept: application/json. Files support GET, HEAD, one HTTP byte range, and raw streaming PUT uploads. Existing names receive a numbered suffix. Delete, rename, and folder creation are not available.
Chrome writes downloads to temporary Droplet staging. After Chrome reports completion, WebBrain uploads the file to shared storage and removes the local copy only after confirmation. Pause is refused while staging or sync work remains.
Client helpers
Node.js and PHP expose listDownloads, uploadDownloadsFile, and downloadDownloadsFile. Python exposes list_downloads, upload_downloads_file, and download_downloads_file. Set browserLocal: true in Node.js, browser_local=True in Python, or the PHP upload helper's final argument to true for a browser-local upload. All transfer file bodies as streams and protect existing local files unless overwrite is explicitly enabled.
Automation
Run a task
Runs are asynchronous by default and return 202 Accepted with a run_id. Poll the run endpoint, or set wait: true to wait until completion or until WebBrain needs user input.
Lets WebBrain open pages, click, type, upload, and complete browser tasks.
Answers questions about an already-open tab without navigation or page-mutation tools.
| Field | Required | Purpose |
|---|---|---|
task | One of | The natural-language browser task. Supply exactly one of task and workflow_id. |
workflow_id | One of | An owned saved workflow to replay. |
parameters | Workflow only | Transient string values keyed by declared parameter ID. |
mode | No | act (default) can interact with pages; ask is read-only on the current tab. Saved workflows always use Act. |
wait | No | Wait for a terminal response instead of returning immediately. |
timeout_ms | No | Maximum time for the blocking request path. |
tab_id | No | Target a specific tab. Otherwise the visible active page is used. |
output_schema | No | Require a validated JSON result. |
capture | No | video records the run; none is the default. |
api_mutations_allowed | No | Opt in to consequential HTTP API mutations for this run. Defaults to false. |
/api/browser-sessions/:sessionId/runsStart a run./api/browser-sessions/:sessionId/scheduled-jobs?job_id=...Read status and outcomes for up to 100 explicit scheduled-job IDs./api/browser-sessions/:sessionId/runs/:runIdRead a run./api/browser-sessions/:sessionId/runs/:runIdDelete a finished run immediately./api/browser-sessions/:sessionId/runs/:runId/messagesAppend a turn after the run finishes./api/browser-sessions/:sessionId/runs/:runId/responsesAnswer its pending clarify_id./api/browser-sessions/:sessionId/runs/:runId/abortAbort a run.Scheduled-job reads require one or more repeated job_id query parameters and return only those requested IDs. Ask mode does not expose navigation or page-mutation tools. To inspect a particular page in Ask mode, first open it with an Act run, then start an Ask run against the returned tab_id. Structured output_schema is supported in both modes. If the connected browser predates run-mode support, Ask returns 409 rather than silently running in Act.
The platform sanitizes cloud trace fields again before persistence. Finished run records are retained for seven days by default and can be deleted sooner through the run DELETE endpoint; active runs cannot be deleted.
Post a new task to /messages after a run is completed, failed, or aborted. WebBrain creates an immutable child run with parent_run_id and reuses the same tab, conversation, and mode unless a new mode is supplied. Append later turns to the newest child. Use /responses only for a paused needs_user_input run.
Repeat safely
Compile and replay saved workflows
Create a workflow from a completed successful cloud run with POST /api/workflows, or import the raw sanitized webbrain-workflow/1 JSON definition with POST /api/workflows/import. Portable files are limited to 1 MiB, never contain runtime parameter values, and receive a fresh cloud ID and timestamps.
- Complete a good runUse Act mode and verify that the run reached the intended result.
- Compile its traceCreate a workflow from the successful session and run IDs.
- Replay with parametersOpen a compatible start page and provide only the declared transient values.
/api/workflowsCompile and store an exact successful run trace./api/workflows/importValidate and store a portable workflow definition./api/workflows?limit=&offset=List workflow metadata./api/workflows/:workflowIdRead metadata and the sanitized definition./api/workflows/:workflowId/exportDownload the raw portable workflow JSON./api/workflows/:workflowIdRename a workflow./api/workflows/:workflowIdDelete the definition while retaining historical runs.Before replay, open a page matching the workflow's start URL family in a compatible login/profile. Start it with workflow_id and parameters; output_schema is not supported for workflow runs in v1. Parameter values are validated before dispatch and are never stored or returned. A pre-upgrade runtime returns 409 for workflow operations but remains usable for ordinary tasks.
Typed results
Structured output
Use shorthand fields such as string, number, boolean, string[], and optional string?. The supported JSON Schema subset includes type, properties, required, items, enum, and additionalProperties.
Run state
Know when it is done
Keep polling while a run is active. Stop when it reaches a terminal state, or answer the exact clarification ID when WebBrain pauses for input.
| Status | Terminal? | What your code should do |
|---|---|---|
running | No | Continue polling the run. |
needs_user_input | No | Read pending_input and post its clarify_id with the user's answer. |
completed | Yes | Read result, summary, and final_url. |
failed | Yes | Inspect error; correct the task, page state, or browser state before retrying. |
aborting | No | Wait until the abort finishes. |
aborted | Yes | Start a new run if more work is needed. |
A terminal response includes result, summary, final_url, and any failure detail in error.
Recovery
Errors tell you what to fix
API failures return JSON with an error message. State conflicts may include extra runtime detail so your code can decide whether to wait, reconnect, or ask the user to intervene.
400401402404409503Open Agent Skill
Give your agent a cloud browser
The website publishes a portable webbrain-cloud skill for Codex, ChatGPT, OpenClaw, Claude Code, and Claude chat or Cowork. It teaches the agent session selection, readiness polling, run continuations, clarification handling, file transfer, cleanup, and the security boundaries around browser actions.
Choose macOS/Linux or Windows PowerShell below and install the skill, then provide WEBBRAIN_API_KEY through the agent runtime's environment or secret manager. Do not put the key inside SKILL.md, its ZIP, a prompt, or a tracked file. The runtime also needs Node.js 18+ and outbound HTTPS access to webbrain.cloud.
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
mkdir -p "$HOME/.agents/skills"
cp -R webbrain-platform/.agents/skills/webbrain-cloud "$HOME/.agents/skills/"
curl --fail --location \
https://webbrain.cloud/skills/webbrain-cloud.zip \
--output webbrain-cloud.zip
# In ChatGPT, open Plugins > Skills > Create > Upload
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
openclaw skills install \
./webbrain-platform/.agents/skills/webbrain-cloud \
--as webbrain-cloud --global
# Claude Code discovers .claude/skills when used inside this repository.
# For a global Claude Code install:
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
mkdir -p "$HOME/.claude/skills"
cp -R webbrain-platform/.agents/skills/webbrain-cloud "$HOME/.claude/skills/"
# Claude chat or Cowork: upload the ZIP in Customize > Skills
curl --fail --location \
https://webbrain.cloud/skills/webbrain-cloud.zip \
--output webbrain-cloud.zip
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
$skillsDir = Join-Path $HOME ".agents\skills"
New-Item -ItemType Directory -Force $skillsDir | Out-Null
Copy-Item -Recurse -Force ".\webbrain-platform\.agents\skills\webbrain-cloud" -Destination $skillsDir
Invoke-WebRequest -Uri "https://webbrain.cloud/skills/webbrain-cloud.zip" -OutFile ".\webbrain-cloud.zip"
# In ChatGPT, open Plugins > Skills > Create > Upload
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
openclaw skills install ".\webbrain-platform\.agents\skills\webbrain-cloud" --as webbrain-cloud --global
# Claude Code discovers .claude\skills when used inside this repository.
# For a global Claude Code install:
git clone --depth 1 https://github.com/esokullu/webbrain-platform.git
$skillsDir = Join-Path $HOME ".claude\skills"
New-Item -ItemType Directory -Force $skillsDir | Out-Null
Copy-Item -Recurse -Force ".\webbrain-platform\.agents\skills\webbrain-cloud" -Destination $skillsDir
# Claude chat or Cowork: upload the ZIP in Customize > Skills
Invoke-WebRequest -Uri "https://webbrain.cloud/skills/webbrain-cloud.zip" -OutFile ".\webbrain-cloud.zip"
Codex and OpenClaw discover the checked-in .agents/skills location, while Claude Code uses the checked-in .claude/skills loader. ChatGPT, Claude chat, and Cowork accept the packaged ZIP through their Skills UI; code execution and network access must be enabled.
No dependencies
Use your language
The repository includes small clients with the same core operations: session creation, pause and resume, readiness, runs, follow-up turns, polling, aborting, structured output, noVNC links, and streaming private Downloads transfers.