Protocol
SDK, then remote MCP.
Unbrowse is an authenticated action surface. The SDK is the product. MCP is a thin remote harness over it. Agent Skills are the YAML those harnesses already are.
Canonical REST
All agent traffic goes through /api/v1. The authenticated principal determines the workspace — a caller-supplied workspace id is never authority.
POST /api/v1/runs
GET /api/v1/runs/:id
POST /api/v1/runs/:id/responses
POST /api/v1/runs/:id/cancel
GET /api/v1/runs/:id/events
POST /api/v1/capabilities/search
POST /api/v1/accounts/connections { origin, username, password } → vault:// ref
POST /api/v1/accounts/register { origin, username } → generated, vaulted
GET /api/v1/vault refs + audit, never secrets
POST /api/v1/learn { har | traces, goal?, title? } → learned.* capability
GET /api/v1/learned
GET /api/v1/learned/:id/harness.yaml
GET /api/v1/learned/:id/skill.md
GET /api/v1/usage verified calls this month, rendered, passthrough, quota
GET /api/v1/sites?q= compiled sites (public registry) with tool counts
GET /api/v1/sites/:host that site's tools
GET /api/v1/sites/:host/openapi.json OpenAPI 3.1: one operation per tool, typed input and output
POST /api/v1/sites/:host/call/:tool run a tool (same metered, verified run)
/api/v1/sites/:host/mcp the site as its own MCP server
POST /api/v1/org { name, appName?, domain? } → your org (see "For agent builders")
GET /api/v1/org users, usage per user, shared tools, keys, balance
POST /api/v1/org/keys mint an org key (a signed-in person only)
POST /api/v1/org/keys/revoke { id }
POST /api/v1/org/users { user } → a key for one of your users
POST /api/v1/org/settings { sharePublic }
GET /api/v1/connect/:id a one-time connect link: app, site, status (no sign-in)
GET /api/v1/replays?source=&site=&outcome=&hasError=&from=&to=&limit= recorded sessions
GET /api/v1/replays/:sid one session: details, signals, summary
GET /api/v1/replays/:sid/events the rrweb recording, agent steps and screenshots
GET /api/v1/replays/:sid/timeline ?from=&to=&kinds=&format=text the session as text lines
GET /api/v1/replays/:sid/shots?key= one agent step screenshot (JPEG)
POST /api/v1/replays/search { q, filter? } → moments with a time into the session
POST /api/v1/replays/ask { question, filter? } → answer with [session, time] citations
DELETE /api/v1/replays/:sid erase a session
Authorization: Bearer ub_live_…
X-Unbrowse-End-User: <your user's id> (org keys: act as that user)TypeScript SDK
import { Unbrowse } from "@unbrowse/sdk";
const ub = new Unbrowse({
apiKey: process.env.UNBROWSE_API_KEY!,
baseUrl: "https://unbrowse.ai/api/v1",
});
const run = await ub.run({
task: "top stories on Hacker News",
interactionMode: "unattended",
idempotencyKey: "hn-1",
});
if (run.status === "input_required") {
await ub.resume(run.runId, run.stateRevision, [{
requirementId: run.requirements[0].id,
expectedRevision: run.requirements[0].revision,
action: "accept",
values: { export_format: "csv" },
}]);
}MCP tools
Remote MCP at /mcp is Streamable HTTP (Grok, Claude, Cursor). /api/mcp is the same adapter over JSON-RPC. authorization and run actor. When three or fewer skills match, dedicated unbrowse.skill.* tools are listed with slot schemas from the harness YAML.
unbrowse.run— start a durable run; first request is fulfilled while indexingunbrowse.inspect— status, requirements, verified resultunbrowse.resume— answer progressive fields on the same rununbrowse.cancel— stop new dispatches, return an effect receiptunbrowse.scrape— one page as clean markdown (main content by default), with links and metadata; plain HTTP when the server's HTML has it, otherwise the cloud browser renders itunbrowse.map— a site's URLs from its sitemaps and the page's links, same-site and filterableunbrowse.sites— what Unbrowse knows about each site before you act: public or behind a sign-in, the kept session (active, expired, logged out), the last sign-in, saved logins, tools already learned there, and bot checks.unbrowse.credits— free credits left this month (Unbrowse calls only) and paid credits (never expire), recent history, and a Stripe Checkout link for a packunbrowse.usage— this month's verified calls, rendered runs and their passthrough cost, and the quota left. Only verified successes billunbrowse.forget— delete one of your own learned capabilities (or unpin a public one)unbrowse.credentials.list,unbrowse.credentials.request,unbrowse.credentials.status— the password manager: saved logins as masked hints, and a one-time link for the person to save a login the agent needs. Values are filled into pages (browse.actautofill, orvaulton a field) and never returned to the modelunbrowse.discover— private space, then public registry. Each learned capability carrieshints: health from its run ledger,warm/rendered/cold, p50/p95 latency and the next stepunbrowse.browse.open,unbrowse.browse.snapshot,unbrowse.browse.act,unbrowse.browse.finish,unbrowse.browse.close— drive a recorded patchright cloud browser with@refsnapshots; the first task is fulfilled and finish (or close) compiles the site into alearned.*capability — an API call or a server-rendered results page, returned as{ title, text, links }. Logins fill from the vault (vault: "password"), never from the agentunbrowse.index,unbrowse.index.status— cover a whole site ahead of need: a background job where Unbrowse's own agent performs the site's core read-only capabilities, proves each with a browserless replay and adds them to your toolsunbrowse.replay.list,unbrowse.replay.search,unbrowse.replay.get,unbrowse.replay.timeline,unbrowse.replay.ask— session replay: find recorded sessions and moments, read a session as a text timeline, get its summary (intent, outcome, drop-off, bugs) and ask questions across sessions with cited moments. Your own cloud-browser sessions become replays (sida_<browse session id>); visitor sessions on unbrowse.ai are admin-only. Alsoclient.replays.*in the SDK andunbrowse replay …in the CLIunbrowse.learn— compile HAR files or recorded traces into a one-calllearned.*capability; see the live demo
Sites as tools
Unbrowse is a browser engine and a compiler: it compiles a website's own requests into an API, and the API into tools. Every compiled site is an MCP server at /api/v1/sites/<host>/mcp and an OpenAPI 3.1 document at /api/v1/sites/<host>/openapi.json; each tool carries its input schema and an output schema learned from verified responses. The public registry holds pre-indexed sites (search and page reads, re-verified on a schedule); your own learned capabilities appear as my__… tools in the main server, tools shared inside your org as org__…, and public ones you use stay in your tool list.
For agent builders
Building agents for other people? Create an org at /app/org and run Unbrowse for all of your users under one key and one bill.
- Send the org key with
X-Unbrowse-End-User: <your user's id>(REST or MCP). Each call runs in that user's own workspace, created on first use: their logins and sessions never mix with anyone else's. Clients that cannot send headers get a per-user key fromPOST /api/v1/org/users. - When a site needs your user's login, the run returns
signIn.url, a one-time/connect/…link that says “Your App wants to use your site account”. Your user needs no Unbrowse account; the login is sealed in their workspace and never reaches you or your model. - Every user's verified calls come out of the org's balance: its free allowance, then paid credits. Usage is shown per user; adding user ids adds no allowance.
- A read-only route one user teaches Unbrowse is shared with your other users as an
org__…tool, scrubbed of their data, never with a login. It goes public only if the org opts in.
Full guide: docs/orgs.md.
Logins
Unbrowse is a remote MCP server: agents call it, and it signs in to sites for them in its own cloud browser, or replays the site's API with the kept session. Agents never receive a password — they get a masked hint and which fields were filled. You keep logins in the password manager; Unbrowse seals each one (AES-256-GCM under a per-workspace key), types it into the site's own login form, and logs every use. Learned tools that sign in take the login from the vault by themselves.
- When a site needs a login you have not saved, the agent gets a one-time link (MCP error
-32042, or “open this link in the user's browser”). You save it on unbrowse.ai and the agent continues — it never sees the value. - Import from KeePassXC, 1Password, Bitwarden, Chrome, Firefox, Apple, LastPass or Dashlane (CSV).
Statuses
Every error code, and what to do about each: Errors & statuses.
- succeeded
- Declared business outcome independently verified. HTTP 200 is not enough.
- input_required
- Waiting on a versioned requirement. Not a capability failure. Resume the same run.
- outcome_unknown
- A mutation may have landed. Automatic mutating retries stay suspended until reconciliation.
- failed / cancelled
- Known effects are retained. Cancellation cannot unsend a delivered request.
Harness YAML
unbrowse/v1alpha1 packages declare slots, operations, bindings, guards and independent outcome checks. YAML is authoring; the runtime executes a typed IR. No eval, no shell, no website-provided expressions.