Profiles
Save browser cookies and storage as an encrypted profile, load it into scrapes, sessions and screenshots, and keep it current without losing newer logins.
A profile holds a browser's cookies and storage under a name, so an agent can start signed in. Scrapes, browser sessions and render jobs load profiles by ID or name. Profiles are encrypted in the node's vault, decrypted only inside the browser that loads them, never logged and never sent to a model. They never leave your organization.
Create one from Playwright
A profile's state is Playwright's storageState JSON, uploaded as is. Sign in once with Playwright, export the state, and upload it:
bash# state.json comes from Playwright: await context.storageState({ path: "state.json", indexedDB: true })curl -s https://beta-api.b3os.org/v1/vision/profiles \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d "{\"name\": \"acme-staging\", \"state\": $(cat state.json)}"
tsimport { readFile } from "node:fs/promises";import { B3OS } from "@b3os/sdk";const b3os = new B3OS();const state = JSON.parse(await readFile("state.json", "utf8"));const profile = await b3os.vision.createProfile({ name: "acme-staging", state });console.log(profile.id, profile.domains, profile.stats);
pythonimport jsonfrom b3os import B3OSb3os = B3OS()with open("state.json") as f: profile = b3os.vision.create_profile({"name": "acme-staging", "state": json.load(f)})print(profile["id"], profile["domains"], profile["stats"])
Profiles hold live session cookies, so create them from your own code rather than through an agent. An agent can then use one by name where its tool takes a profile: the remote MCP server's vision_scrape takes the whole scrape request, profile included. The local server's tools take no profile; there, create a session with the profile from your code and give the agent its ID, which vision_act accepts as session_id.
Every cookie domain and storage origin must belong to the profile's domains. Leave domains out and B3OS derives it from the state; a state that strays outside the listed domains fails with profile_domain_mismatch. Names are unique per project. The API returns the domains and counts of cookies, origins and bytes, never the values.
Use a profile
| Where | Field |
|---|---|
| Scrape, batch, crawl | profile (implies the browser tier) |
| Browser session | profile, with persist_profile to save back on close |
| Screenshot, video | profile, or source.profile for videos |
Save from a session
Sign in inside a session (an agent, or a person through the CDP socket), then save its current cookies and storage:
bashcurl -s "https://beta-api.b3os.org/v1/vision/sessions/$SESSION_ID/profile" \ -H "Authorization: Bearer $B3OS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "acme-staging"}'
This creates the profile if the name is new, or writes a new revision if it exists. A session created with profile and persist_profile: true does the same automatically when it closes.
Revisions and conflicts
Every save increments the profile's revision. Two sessions can load the same revision; if one saves first, the other's save would throw away newer state, so it fails with profile_conflict. Save with overwrite (or create the session with overwrite_profile) to replace it anyway, or load the profile again in a new session. While a session with persist_profile holds a profile, its status is locked; other loads still see the last saved revision.
Change, export or delete
PATCH /v1/vision/profiles/{profile_id}renames it, changes its domains, expiry or metadata, or replaces its state with a new revision.GET /v1/vision/profiles/{profile_id}/stateexports the current state in the samestorageStateshape, so you can load it into your own browser. It contains live session cookies: treat the response like a password. It needsvision:write, the scope that can replace the state.DELETE /v1/vision/profiles/{profile_id}deletes the profile and every revision. Sessions that already loaded it keep their cookies until they close.
A profile can carry an expires_at, after which it is deleted.