Sessions
A session is one cloud browser. How it starts, how long it lives and how it ends.
A session is one browser that webagent runs for you in the cloud. You open it, give it work (a goal, a skill, plain actions), and close it. Everything else, such as tasks, live view and evidence, belongs to a session.
Session ids look like sess_us_…. The part after sess_ is the region.
Open a session
You get a session in three ways:
- Call the
create_sessionMCP tool, orPOST /v1/sessions. You choose the options and keep the session until you stop it. See Tools. - Call
run_taskwithout asessionId. The tool opens a session, runs the goal and closes the session after the task unlesskeepOpenistrue. It takes the common options itself:record,headers,proxyUrl,allowHostsandcredentials. - Run a skill (including
smoke_test), or a quick action such asscrape. Each run gets its own session. You do not manage it.
A session opens in Chromium. Firefox and WebKit are not available yet. The create call takes options such as startUrl, device, viewport, record, headers, proxyUrl, locale and timezone. The full list is in the API reference.
Every page the browser opens goes through a guard. Private, loopback and link-local addresses are blocked. A cloud browser cannot open localhost on your machine. See Test a local app.
States
| Status | Meaning |
|---|---|
starting | The browser is being placed and started |
live | Ready. It takes actions and tasks |
paused | A person took over. The agent waits, and agent actions are refused with session_paused |
ended | Closed. The record stays |
lost | The browser went away without a clean close |
An ended session has an endReason:
endReason | Meaning |
|---|---|
closed | You stopped it |
idle_timeout | No action for the idle time. See How long a session lives |
max_duration | It reached its maximum age |
runner_lost | The browser went away without a clean close |
error | Something failed |
memory_limit | It went over its memory cap a second time |
capacity_timeout | It waited in the queue and no browser freed up. See When no browser is free |
maintenance | The machine that runs the browser was taken out of service. See Maintenance |
revoked | The MCP sign-in the session was started under was revoked |
maintenance and revoked are not errors.
Maintenance
Lookover sometimes takes a browser machine out of service, for example for an update. The sessions on it end in this order:
- A step in progress finishes. A task is not cut in the middle of a step.
- Idle sessions end first. The rest end at a set time, with the end reason
maintenance. - The recording and logs are uploaded and billing stops, as for any ended session.
About 5 minutes before that time, the live view socket (endpoints.live) sends one message to everyone connected:
{ "type": "maintenance_warning", "endsAt": 1800000000000 }endsAt is a Unix time in milliseconds. The dashboard live view shows it as "This browser restarts for maintenance at" and the local time, with the note "Finish what you are doing, then start a new session." If your own client reads the live socket, do the same: finish before endsAt, then open a new session. An ended session does not come back. The dashboard shows it as "Ended for maintenance".
Revoked
When an MCP sign-in is revoked, the sessions started under it end with revoked. The dashboard shows "Ended: connection revoked".
When no browser is free
By default, a create call fails with capacity_exceeded when every browser is busy. Two options change that:
waitForCapacityMs(0 to 30 000): wait up to this long for a browser to free up, then fail.queueForCapacity({ "maxWaitSeconds": 1 to 900 }): keep the session in a queue. Not together withwaitForCapacityMsabove 0.
A queued session has the status starting and a queue object with position, etaSeconds, reason (vpc_full or scaling_up, when a new browser is starting up), queuedAt and expiresAt. The queue object goes away when the session starts. Read the session with GET /v1/sessions/:id until queue is gone and the status is live, then ask for a token with POST /v1/sessions/:id/token. A session that finds no browser before expiresAt ends with the end reason capacity_timeout. A quota refusal (too many open sessions, memory, budget or credits) is not queued: it fails at once. When the platform is under high demand, the open-session limit can drop for a while: see High demand and requesting more sessions.
How long a session lives
A session ends on its own in two cases:
- Idle. No action for 5 minutes. Change it with
idleTimeoutMs. - Maximum age. One hour after it started. Change it with
maxDurationMs.
Both options accept 10 seconds up to 24 hours. These are today's defaults and they may change.
A session that uses more memory than its cap is stopped the second time it goes over the cap, with the end reason memory_limit.
Stop a session
Call the stop_session MCP tool, or DELETE /v1/sessions/:id. Stopping is safe to repeat. A session you forget is closed by the idle and age limits above, but it holds browser capacity and counts as compute until then. See Limits and Billing.
Talk to a live session
POST /v1/sessions returns endpoints and a short-lived token. They let a client talk to the browser directly. For most uses you do not need them: the MCP tools and the dashboard do this for you. To get a fresh token, call POST /v1/sessions/:id/token.
Connect over CDP
endpoints.cdp is a WebSocket address. Every connection needs the session token from the same reply (not your API key) in an Authorization: Bearer <token> header. A connection with no header is refused with 401. With Playwright, pass the header in connectOverCDP:
import { chromium } from "playwright";
// `cdp` and `token` come from the POST /v1/sessions reply (endpoints.cdp and token).
const browser = await chromium.connectOverCDP(cdp, {
headers: { Authorization: `Bearer ${token}` },
});A client that cannot set headers, such as a browser page, sends the token as a WebSocket subprotocol instead: ["webagent", "webagent.bearer.<token>"]. Never put the token in the URL. The token is short-lived: when tokenExpiresAt is near, call POST /v1/sessions/:id/token for a new one.
Read sessions back
GET /v1/sessions lists sessions, GET /v1/sessions/:id reads one. The list_sessions and get_session MCP tools do the same. Each session may have a title. It is made from the first goal you give it, or you can set it with PATCH /v1/sessions/:id.