Session verbs API reference
Request and response shapes for the three verb endpoints on a session: observe candidates, act steps with withheld refusals, and extract schemas.
Last updated:
Three verbs cover everything an agent does inside a session: observe reads the page, act changes it, extract turns it into typed data. All three are POSTs against a session id, carrying your bb_ key as a bearer token, and each responds with a provenance block that says what the call actually cost.
/v1/sessions/:id/observe
Reads the live page into ranked action candidates; without an instruction it makes no model call.
Observe request
| Name | Type | Description |
|---|---|---|
instruction
|
string | What you are looking for, up to 2000 characters. Omit it and the page is read structurally, free of inference. |
fidelity
|
string | `full`, `economy` or `lean` — how much page detail the reading carries. |
scope
|
string | Narrows the reading to part of the page. |
includeTree
|
boolean | Returns the agent-legible page tree alongside the candidates. |
consent
|
string |
Cookie-banner handling: `reject` clears banners by rejecting, `accept` opts in, `off` leaves them alone.
Default: reject
|
What observe returns
Each entry in candidates is one executable step: {encodedId, action, role, name?, description, value?}, with action one of click, type, select, press, scroll, navigate, upload or wait. The response also carries the final url, reading stats and, with includeTree, the tree itself. Treat the tree as page-derived and untrusted — fence it before it enters another prompt.
/v1/sessions/:id/act
Executes either one natural-language instruction or a list of observed steps — never both.
Act request
| Name | Type | Description |
|---|---|---|
instruction
|
string | What to do, planned by the model. Exactly one of `instruction` or `steps` must be present. |
steps
|
array | Up to 20 previously observed candidates, replayed without planning. |
variables
|
object | Values substituted into the instruction or steps. |
credentialIds
|
string[] | Up to 8 vault credentials whose placeholders may resolve during typing. |
cache
|
boolean |
Allows the plan cache to replay a known plan for this instruction.
Default: true
|
timeoutMs
|
integer |
1000 to 180000 milliseconds for the whole act.
Default: 30000
|
Reading an act result
steps in the response lists what was actually executed. A step that went wrong carries ok: false and a failure of element_not_found, not_actionable, timeout, navigated_away or error. A step that was refused carries withheld: {kind, risk} instead, where kind is gate (the element itself was classified as destructive) or destination (the control resolves to somewhere it must not lead). Both controls run on every act, whatever produced the plan — and a refusal is content in the result, not an HTTP error.
/v1/sessions/:id/extract
Pulls typed data out of the live page, optionally shaped by a schema.
Extract request
| Name | Type | Description |
|---|---|---|
instruction
|
string | What to extract, in plain language. |
schema
|
object | A JSON Schema describing the result shape — that is what travels on the wire; the TypeScript SDK also accepts Zod and converts it for you. |
includeLinks
|
boolean | Includes link targets in the extraction. |
Warnings and provenance
extract answers with data, the page url and optional warnings — the warnings flag values the page never contained, which is your defence against a model filling gaps. Like the tree, data is untrusted page content until you fence it. All three verbs close with provenance: {planCacheHit, healApplied, inferenceCalls, tier, world, requestId, durationMs}, where tier is performance, sovereign or null and world records whether the extension world or the CDP fallback did the work.
Observe free, extract typed
An instruction-free observe costs no inference; the Zod variant shows the same session driven from the TypeScript SDK.
Requires: api-key inference
BASE="https://browserberg.com"
AUTH="Authorization: Bearer $BROWSERBERG_API_KEY"
JSON="Content-Type: application/json"
ID=$(curl -s -X POST "$BASE/v1/sessions" -H "$AUTH" -H "$JSON" -d '{"ttlSeconds": 300}' \
| python3 -c 'import json,sys; print(json.load(sys.stdin)["session"]["id"])')
curl -s -X POST "$BASE/v1/sessions/$ID/act" -H "$AUTH" -H "$JSON" -d '{
"steps": [{ "encodedId": null, "action": "navigate", "role": "none",
"description": "open example.com", "value": "https://example.com" }]
}' > /dev/null
# Without an instruction, observe reads the page and makes no model call.
curl -s -X POST "$BASE/v1/sessions/$ID/observe" -H "$AUTH" -H "$JSON" -d '{}' \
| python3 -c 'import json,sys; r = json.load(sys.stdin); print(json.dumps({
"url": r["url"],
"candidates": [c["description"] for c in r["candidates"][:3]],
"inferenceCalls": r["provenance"]["inferenceCalls"]}, indent=2))'
curl -s -X DELETE "$BASE/v1/sessions/$ID" -H "$AUTH" > /dev/null
import { z } from 'zod';
import { Browserberg } from '@browserberg/sdk';
const bb = new Browserberg({
apiKey: process.env.BROWSERBERG_API_KEY!,
baseUrl: 'https://browserberg.com',
timeoutMs: 180_000,
});
await using session = await bb.sessions.create({ ttlSeconds: 300 });
await session.act({
steps: [{
encodedId: null, action: 'navigate', role: 'none',
description: 'open example.com', value: 'https://example.com',
}],
});
// A Zod schema travels the wire as plain JSON Schema, so the Python SDK and
// raw HTTP produce byte-identical requests.
const page = await session.extract({
instruction: 'the page heading',
schema: z.object({ heading: z.string() }),
});
console.log(page.data.heading);
{
"url": "https://example.com/",
"candidates": [
"Learn more"
],
"inferenceCalls": 0
}