> ## Documentation Index
> Fetch the complete documentation index at: https://usenaive.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating from Browserbase to Naive

> Move agent web automation from a Browserbase + Stagehand account to Naive's browser primitive — the same drive-a-cloud-browser-with-natural-language capability, but the session, its saved logins, and the credentials it creates all live under one governed identity that also owns the agent's email, cards, and vault.

<Frame caption="Browserbase cloud sessions + Stagehand → the Naive browser primitive">
  <img className="block dark:hidden" src="https://mintcdn.com/naive/fqmboaBqXt44Y7w1/migration-guides/logos/browserbase-light.svg?fit=max&auto=format&n=fqmboaBqXt44Y7w1&q=85&s=0c6c46ee9cf5772bb9099ce8d85f3370" alt="Browserbase" height="28" data-path="migration-guides/logos/browserbase-light.svg" />

  <img className="hidden dark:block" src="https://mintcdn.com/naive/fqmboaBqXt44Y7w1/migration-guides/logos/browserbase-dark.svg?fit=max&auto=format&n=fqmboaBqXt44Y7w1&q=85&s=b1ca9e3e8db5feb19c1c04ff0ebbf33d" alt="Browserbase" height="28" data-path="migration-guides/logos/browserbase-dark.svg" />
</Frame>

[Browserbase](https://docs.browserbase.com) gives an AI agent a real cloud browser: create a
session, connect over CDP, and drive it — usually with [Stagehand](https://docs.stagehand.dev),
Browserbase's automation framework whose `act` / `extract` / `observe` verbs let you script the
page in natural language instead of brittle selectors. It does that job well, and the API is
mature. But it is also a *separate vendor account*:

* The session lives behind a **`BROWSERBASE_API_KEY` + project ID**, its own console, and its own
  billing — disconnected from wherever the agent's email, cards, secrets, and KYC live.
* The interesting workflows — *sign this agent up for a SaaS, then log it back in later* — are
  **yours to build**: you generate the password, you decide where to store it, and you own the risk
  of it leaking. Browserbase persists *cookies* via [Contexts](https://docs.browserbase.com/platform/browser/core-features/contexts);
  it does not vault the credential itself.
* "Who let this agent create an account, and what *else* can it touch?" is answered in Browserbase
  for browser sessions, and in unrelated systems for everything else. The session has no shared
  accountability with the rest of the agent's footprint.

Naive's [`browser`](/getting-started/browser) primitive gives the agent the **same** capability —
a cloud browser it drives with `navigate` / `act` / `extract` / `observe` — but the session is
**rooted in one governed identity**:

* The [tenant user](/getting-started/users) that owns the session is the same user that owns its
  [email inboxes](/getting-started/email), its [cards](/getting-started/cards), its
  [vault](/getting-started/vault) secrets, and its [KYC](/getting-started/verification).
* Autonomous [`signup`](/getting-started/browser#autonomous-signup--login) generates a strong
  password, fills the form, and **vaults `{ email, password }` server-side** under that identity —
  the password never reaches your code or the model. A later `login` reads it back the same way.
* Whether an agent may browse at all — which domains, whether it can submit forms, whether creating
  an account freezes for human approval — is decided by that user's
  [Account Kit](/getting-started/account-kits) and the session's allowlist **at execution time**.

This guide maps Browserbase + Stagehand to Naive's browser primitive, shows the smallest working
swap, and is explicit about what does not map yet.

<Note>
  Browserbase and Stagehand are trademarks of their respective owners, used here for identification only. No endorsement or affiliation is implied.
</Note>

<Info>
  **Tested against:** the Browserbase Node SDK [`@browserbasehq/sdk`](https://github.com/browserbase/sdk-node)
  **v2.x** (`bb.sessions.create`, `bb.contexts.create`, base `https://api.browserbase.com`) and
  [Stagehand](https://docs.stagehand.dev) [`@browserbasehq/stagehand`](https://www.npmjs.com/package/@browserbasehq/stagehand)
  **v3** (`stagehand.act` / `extract` / `observe` on the instance; docs snapshot June 2026), and the
  Naive Node SDK [`@usenaive-sdk/server`](/sdk/overview) against the Naive API (base
  `https://api.usenaive.ai/v1`, docs snapshot June 2026).

  Version notes:

  * **Stagehand v3** moved `act` / `extract` / `observe` from the `page` object to the `stagehand`
    instance and unified model config into a single `model` option. v2 called `page.act({ action })`.
    This guide uses the **v3** shape.
  * Browserbase **separates concerns**: the SDK opens the session (`sessions.create` → `connectUrl`),
    you connect with **Playwright/Puppeteer over CDP**, and Stagehand layers the NL verbs on top.
    Naive folds *open + drive + autonomous signup/login* into **one** per-user primitive with **no
    CDP/Playwright wiring**.
  * Browserbase identifies a session by its **`id`** and persists login state via the **Contexts
    API**. Naive returns a **`session_id`**, enforces a **domain allowlist (default-deny)**, and
    persists logins as **human-granted saved contexts** — an agent can *use* one but generally cannot
    create the grant itself.
  * Naive sessions have a **hard TTL** (default 15 min, max 30) and a **per-session credit ceiling of
    40**. Browserbase supports much longer sessions (up to 6h) and `keepAlive` — see
    [what doesn't map yet](#what-does-not-map-yet).
</Info>

## Concept map

| Browserbase / Stagehand                                                                            | Naive                                                                                      | Notes                                                                                                            |
| -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- |
| `new Browserbase({ apiKey })` + `new Stagehand({ env, apiKey, model })`                            | `new Naive({ apiKey })`                                                                    | Server-side credential in both cases                                                                             |
| `BROWSERBASE_API_KEY` + **project ID** — one Browserbase account, isolated from your other vendors | [tenant user](/getting-started/users) via `naive.forUser(id)` — scopes **every** primitive | The core consolidation win                                                                                       |
| **Project** as the unit of grouping (browser sessions only)                                        | [Tenant users](/getting-started/users) (`naive.forUser(id)`)                               | Naive's unit of isolation spans *every* primitive, not just the browser                                          |
| `bb.sessions.create({ projectId, browserSettings })` → `connectUrl`                                | `client.browser.createSession({ allowed_domains, allow_writes, timeout })` → `session_id`  | Naive sessions are domain-allowlisted (default-deny) and per-user                                                |
| `chromium.connectOverCDP(session.connectUrl)` + Playwright `page`                                  | (none)                                                                                     | No CDP/Playwright wiring — you call verbs directly on the `session_id`                                           |
| `await stagehand.init()` (launches the model + browser)                                            | (folded into `createSession`)                                                              | No separate model key — Naive runs the automation model server-side                                              |
| `page.goto(url)`                                                                                   | `client.browser.navigate(sessionId, url)`                                                  | Allowlist + SSRF denylist enforced on every navigation                                                           |
| `stagehand.act("click …")`                                                                         | `client.browser.act(sessionId, "click …")`                                                 | Natural-language action; submits/destructive actions need `allow_writes`                                         |
| `stagehand.extract(instruction, zodSchema)`                                                        | `client.browser.extract(sessionId, instruction)`                                           | Read-only; **no schema arg** — see [gaps](#what-does-not-map-yet)                                                |
| `stagehand.observe("find …")`                                                                      | `client.browser.observe(sessionId, "find …")`                                              | List candidate elements/actions (read-only)                                                                      |
| `page.screenshot()`                                                                                | `client.browser.screenshot(sessionId)`                                                     | Short-lived signed URL                                                                                           |
| `stagehand.close()` / session timeout                                                              | `client.browser.closeSession(sessionId)`                                                   | Always close — idle sessions bill a vendor **time floor** at close                                               |
| `bb.sessions.list()`                                                                               | `client.browser.listSessions()`                                                            | Sessions for the identity                                                                                        |
| **Build it yourself:** `act` + your own password + your own secret store                           | `client.browser.signup({ service, url })`                                                  | Generates a strong password, fills the form, **vaults the credential** server-side                               |
| **Re-use a Context** to land logged-in                                                             | `client.browser.login({ service, url })`                                                   | Reads the vaulted credential and re-authenticates — password never returned                                      |
| `act("…", { variables: { password } })` — secret substituted, **but you supply + store it**        | `%password%` substituted server-side — **you never hold it**                               | Secret never crosses the agent/LLM boundary in either case; ownership differs                                    |
| `bb.contexts.create()` + `browserSettings.context: { id, persist }`                                | Saved logins: `--human-login` → `context save` → `context grant`                           | Persistence is human-granted, default-deny (an agent can use, never grant)                                       |
| Manual login via **Session Live View**, then persist context                                       | Dashboard **Browser tab → Live View → Take control**                                       | One-time human login for SSO / 2FA / CAPTCHA                                                                     |
| `bb.sessions.debug(id)` → live/debugger URL                                                        | Dashboard Live View (watch + take control)                                                 | Human watch/intervene                                                                                            |
| `proxies: true` / geolocation                                                                      | `--proxy` residential proxy (requires an allowlist)                                        |                                                                                                                  |
| API-key scopes / project separation                                                                | **Account Kit** `browser` enabled + per-kit `requiresApproval` for `signup`                | Permission is execution-time policy on the identity — see [gains](#gain-2-execution-time-permission-enforcement) |
| `browserSettings.solveCaptchas`                                                                    | —                                                                                          | No automatic CAPTCHA solving — a human takes control in Live View                                                |
| `stagehand.agent({...}).execute(...)` (CUA multi-step loop)                                        | —                                                                                          | No built-in autonomous multi-step agent — see [gaps](#what-does-not-map-yet)                                     |
| Bring-your-own model / per-call model choice                                                       | —                                                                                          | Automation model is server-side; not selectable                                                                  |
| Session **recording / replay**, **downloads**, **file uploads**, **extensions**                    | —                                                                                          | Not provided — see [gaps](#what-does-not-map-yet)                                                                |
| `timeout` up to **6h**, `keepAlive`                                                                | Hard TTL 15 min (max 30), ceiling 40 credits/session                                       | Re-open sessions; persist with saved logins                                                                      |

## Before / after: the core path

The path that matters for almost every browsing agent is *open a session, go somewhere, do
something, read something back*. Here it is on both platforms.

<CodeGroup>
  ```ts Browserbase + Stagehand theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Stagehand } from "@browserbasehq/stagehand";
  import { z } from "zod";

  // Browserbase account key + a separate LLM key for Stagehand
  const stagehand = new Stagehand({
    env: "BROWSERBASE",
    apiKey: process.env.BROWSERBASE_API_KEY!,
    projectId: process.env.BROWSERBASE_PROJECT_ID!,
    model: "openai/gpt-4.1-mini", // your own model credential
  });

  await stagehand.init();                       // opens the cloud session
  const page = stagehand.context.pages()[0];    // a Playwright page over CDP

  await page.goto("https://news.ycombinator.com");
  await stagehand.act("click the first story link");

  const data = await stagehand.extract(
    "the article title and author",
    z.object({ title: z.string(), author: z.string() }),
  );

  await stagehand.close();
  // → the session is an island behind your Browserbase project + a separate model key.
  ```

  ```ts Naive theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import { Naive } from "@usenaive-sdk/server";

  const naive = new Naive({ apiKey: process.env.NAIVE_API_KEY! });
  const client = naive.forUser("acme"); // same id space as your own users

  // One call opens a domain-scoped, per-user session. No CDP, no model key.
  const session = await client.browser.createSession({
    allowed_domains: ["news.ycombinator.com"],
  });

  await client.browser.navigate(session.session_id, "https://news.ycombinator.com");
  await client.browser.act(session.session_id, "click the first story link");

  const data = await client.browser.extract(
    session.session_id,
    "the article title and author",
  );

  await client.browser.closeSession(session.session_id);
  // → the SAME `client` also owns this user's email, cards, vault, and KYC.
  ```
</CodeGroup>

The drive verbs line up closely. The real differences to plan for:

* **One handle, no plumbing.** Browserbase is *create session → connect over CDP → wrap with
  Stagehand → bring your own model key*. Naive's `createSession` returns a `session_id` you drive
  directly — no Playwright, no CDP, no separate model credential.
* **Sessions are allowlisted by default.** Every Naive session must pass `allowed_domains`
  (default-deny); navigation also runs through an SSRF denylist. Pass `['*']` to browse
  unrestricted (not recommended).
* **Writes are gated.** A destructive/submit `act` is refused unless the session was opened with
  `allow_writes: true`. Reading and clicking links work without it.
* **`extract` is instruction-only.** Naive returns extracted data from a natural-language
  instruction; it does not take a Zod/JSON schema. Validate the shape in your own code — see
  [gaps](#what-does-not-map-yet).
* **The id is your identity, not a separate account.** In Browserbase the API key scopes
  *Browserbase*. In Naive the same `forUser(id)` handle also owns the agent's email, cards, vault,
  and KYC.

## The workflow that actually changes: signup & login

This is where the migration earns its keep. On Browserbase you *build* account creation: generate a
password, drive the form, and decide where to keep the secret. Stagehand can substitute the value
so it never hits the model — but **you still own it**. Naive turns the whole flow into one call
that **vaults the credential under the user's identity**.

<CodeGroup>
  ```ts Browserbase + Stagehand (you own the secret) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  import crypto from "node:crypto";

  const email = "acme-agent@yourdomain.com";
  const password = crypto.randomBytes(18).toString("base64url"); // you generate it

  await page.goto("https://app.example.com/signup");
  // `variables` keeps the value out of the LLM prompt — good — but it is still yours:
  await stagehand.act("fill the email field with %email%",       { variables: { email } });
  await stagehand.act("fill the password field with %password%", { variables: { password } });
  await stagehand.act("submit the signup form");

  // Now YOU must store { email, password } somewhere and protect it,
  // and YOU must wire any human approval before the account is created.
  await myOwnSecretsManager.put("login:app.example.com", { email, password });
  ```

  ```ts Naive (the identity owns the secret) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // Naive generates a strong password, fills the form, and stores { email, password }
  // in THIS user's encrypted Vault under `login:app.example.com`. The password is not
  // returned to your code or sent to the model.
  const res = await client.browser.signup({
    service: "app.example.com",
    url: "https://app.example.com/signup",
  });

  // signup is sensitive, so it is approval-gated by default:
  if (isPendingApproval(res)) {
    // returns HTTP 202 / status "pending_approval" — a human approves, then it runs.
  }

  // Later, from a brand-new session, re-authenticate without ever touching the password:
  await client.browser.login({ service: "app.example.com", url: "https://app.example.com/login" });
  ```
</CodeGroup>

* **Credential custody flips.** Browserbase substitutes the secret at fill-time but leaves you
  holding it. Naive generates *and* vaults it under the tenant user — the agent and the model never
  see the password.
* **Verification emails land in the same identity.** `signup` uses the user's
  [`profile`](/sdk/sub-clients/profile) email as the account email. Point it at a provisioned
  [inbox](/getting-started/email) (`client.profile.setEmail(...)`) so confirmation links arrive in
  a mailbox the *same* user owns.
* **Account creation is approval-gated.** Because `signup` creates a real account under the user's
  identity, it freezes for [human approval](/getting-started/approvals) by default — toggle per
  Account Kit.

## Minimal viable migration

The smallest swap that keeps a working browsing agent running is just *open → drive → read → close*.

<Steps>
  <Step title="Install the SDK and set your key">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    npm install @usenaive-sdk/server
    ```

    Set `NAIVE_API_KEY` (a server-side key from the [dashboard](https://usenaive.ai/developers)).
    You can drop `BROWSERBASE_API_KEY`, the project ID, and the separate model key for this path.
  </Step>

  <Step title="Swap session creation">
    Replace `new Stagehand({...})` + `stagehand.init()` (or `bb.sessions.create()` +
    `chromium.connectOverCDP(...)`) with a single
    `client.browser.createSession({ allowed_domains: [...] })`. Keep the returned `session_id`.
    Add `allow_writes: true` only if the agent submits forms.
  </Step>

  <Step title="Swap the drive verbs">
    Map each call onto the `session_id`:
    `page.goto` → `client.browser.navigate(sessionId, url)`,
    `stagehand.act` → `client.browser.act(sessionId, instruction)`,
    `stagehand.extract` → `client.browser.extract(sessionId, instruction)` (drop the schema),
    `stagehand.observe` → `client.browser.observe(sessionId, instruction)`,
    `page.screenshot` → `client.browser.screenshot(sessionId)`.
  </Step>

  <Step title="Swap teardown">
    Replace `stagehand.close()` with `client.browser.closeSession(sessionId)`. Always close —
    an idle session bills a vendor **time floor** at close (1 credit ≤5 min, 2 ≤15 min, 3 ≤30 min).
  </Step>

  <Step title="Ship it">
    At this point you are off Browserbase for the core open/drive/read path. Everything below is
    upside, not a requirement.
  </Step>
</Steps>

## Consolidate further once you're on Naive

This is where the migration pays for itself. In Browserbase, the API key isolates **browser
sessions and nothing else**, and any account the agent creates is a credential *you* babysit. On
Naive, the unit of isolation is a **tenant user**, and the browser is one of many primitives that
identity owns.

<CodeGroup>
  ```ts Browserbase (browser sessions only) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // One project (or key) per customer isolates browser sessions — and only those.
  const stagehand = new Stagehand({
    env: "BROWSERBASE",
    apiKey: process.env.BROWSERBASE_API_KEY!,
    projectId: customerProjectId,
    model: "openai/gpt-4.1-mini",
  });
  await stagehand.init();
  // ...drive the browser...

  // The card, the vault secrets, the email inbox, and the KYC for this customer's
  // agent live in entirely separate systems with their own tenancy — and the SaaS
  // password the agent just created lives in whatever store you wired up.
  ```

  ```ts Naive (tenant user — the whole stack) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  // One tenant user per customer isolates *every* primitive.
  const acme = await naive.users.create({
    external_id: dbCustomer.id,
    email: dbCustomer.email,
  });

  const client = naive.forUser(acme.id);

  // Browse, then create + vault an account, all under one identity:
  const session = await client.browser.createSession({ allowed_domains: ["app.example.com"] });
  await client.browser.signup({ service: "app.example.com", url: "https://app.example.com/signup" });

  // The SAME client owns this customer's email inbox, card, vault, and KYC —
  // one identity, isolated end to end, and the SaaS credential lives in their vault.
  await client.email.createInbox({ local_part: "agent" });
  await client.cards.create({ spending_limit_cents: 25_000 });
  const creds = await client.vault.reveal("login:app.example.com"); // server-side; not returned to the model
  ```
</CodeGroup>

### Gain #1 — one identity across primitives

* With Browserbase, the agent's sessions are an island behind an API key, and any account it
  creates is a secret you store and protect yourself. With Naive, `naive.forUser(acme.id)` is a
  single handle to **browser *and* email *and* cards *and* vault *and* KYC**.
* The SaaS accounts the agent signs up for are not loose credentials — they are vaulted under the
  identity that owns everything else. Tear the customer down from one place; sibling tenants can
  never read each other's sessions, logins, cards, or secrets.

### Gain #2 — execution-time permission enforcement

* Whether an agent may browse at all, *which* domains it may reach, whether it may submit forms,
  and whether *creating an account* freezes for human review are all **policy on the identity** —
  not checks you hand-write and not API-key scopes you manage in a second console.

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
// A starter tier: browsing allowed, but new-account signup must be approved by a human.
const starter = await naive.accountKits.create({
  name: "Starter",
  primitives_config: {
    browser: { enabled: true, requiresApproval: true }, // signup freezes for approval
    cards: { enabled: false },                           // no card for this tier
  },
});
await naive.accountKits.assignUser(starter.id, acme.id);
```

* The agent's code is identical for every tier — `client.browser.act(...)`, `client.browser.signup(...)`.
  Whether the call runs is decided at execution time:
  * A session must pass its **`allowed_domains`** allowlist (default-deny) and an SSRF denylist —
    no code change on your side.
  * A destructive `act` without **`allow_writes`** is refused.
  * With `browser.requiresApproval: true`, an autonomous **`signup`** returns `pending_approval`
    and runs only after a human [approves](/getting-started/approvals) it. Driving the page is a
    routine action and is **not** approval-gated.
  * **Saved logins are human-granted.** An agent can *use* a saved login but generally cannot
    create the grant or revoke a shared one — that lives in the dashboard.

### Gain #3 — unified accountability

* Every session, navigation, action, and autonomous signup for a customer lands in *one* per-user
  [activity log](/getting-started/logs) — alongside their email, card, vault, and KYC events, not
  in a separate Browserbase console:

```ts theme={"theme":{"light":"github-light","dark":"github-dark"}}
const { events } = await naive.forUser(acme.id).logs.query({ limit: 50 });
// "what did this agent do, for whom?" — browser, email, cards, vault, one timeline
```

* That is the question that is hard to answer when browsing lives in Browserbase, the SaaS password
  lives in your secrets manager, cards live in Stripe, and email lives somewhere else. Under Naive
  it is a single query.

## What does not map yet

A migration guide that hides gaps is worse than none. The core path (open → navigate → act →
extract → close) and the signup/login flow map cleanly, but the following Browserbase / Stagehand
capabilities have **no direct equivalent** on Naive's browser primitive today. Check this list
against your app before you commit.

| Browserbase / Stagehand feature                                                         | Status on Naive                                     | Workaround                                                                                         |
| --------------------------------------------------------------------------------------- | --------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| **Raw CDP / Playwright / Puppeteer** access (`connectUrl`)                              | Not provided — driven via SDK verbs only            | Use `navigate` / `act` / `extract` / `observe`; no arbitrary Playwright calls                      |
| **Stagehand `agent()`** autonomous multi-step loop (Computer-Use Agent)                 | Not provided                                        | Orchestrate the verbs yourself, or drive them from [orchestration](/getting-started/orchestration) |
| **Bring-your-own model** / per-call model choice                                        | Not exposed — the automation model runs server-side | No model selection in the browser verbs                                                            |
| **`extract` with a Zod/JSON schema**                                                    | Instruction-only `extract`                          | Parse + validate the returned data in your own code                                                |
| **Auto CAPTCHA solving** (`solveCaptchas`)                                              | Not automatic                                       | A human clicks **Take control** in the dashboard Live View                                         |
| **Session recording / replay**, **downloads**, **file uploads**, browser **extensions** | Not provided                                        | Screenshot per step; no replay / upload / extension APIs                                           |
| **Long-lived sessions** (`timeout` up to 6h, `keepAlive`)                               | Hard TTL 15 min (max 30), 40-credit ceiling/session | Re-open sessions; persist state via saved logins                                                   |
| **Self-managed Contexts** (create/persist arbitrary cookie state)                       | Saved logins are **human-granted**, default-deny    | A human opens a `--human-login` session, then `context save` + `context grant`                     |
| **Custom fingerprints / `verified` OS spoofing / viewport / regions**                   | Not exposed                                         | Residential `--proxy` only; limited fingerprint control                                            |
| **`userMetadata`** and other session config                                             | Not exposed                                         | Track your own metadata against the `session_id`                                                   |

<Warning>
  If your agent needs **raw Playwright/CDP control**, a **built-in autonomous (CUA) agent loop**,
  **schema-validated extraction**, **model choice per call**, **auto CAPTCHA solving**, **session
  replay/uploads/downloads**, or **multi-hour sessions**, those are the gaps most likely to matter —
  Naive's browser is a **per-user, domain-allowlisted session driven by NL verbs, with autonomous
  signup/login backed by the Vault**. The flip side is the gain: the session, its saved logins, and
  every credential it creates are governed by the *same* identity and Account Kit as the rest of the
  agent — not a standalone account whose secrets you babysit.
</Warning>

## Where to go next

* [`browser` primitive](/getting-started/browser) — full session lifecycle, autonomous signup/login, saved logins, live view
* [`browser` SDK sub-client](/sdk/sub-clients/browser) — typed method signatures
* [`browser` CLI](/cli/browser) — session create/navigate/act/extract, signup/login, context save/grant
* [Vault](/getting-started/vault) — where autonomous signup stores `login:<service>` credentials
* [Account Kits](/getting-started/account-kits) and [Approvals](/getting-started/approvals) — the policy model behind execution-time governance and signup approval
* [Email](/getting-started/email) and [Profile](/sdk/sub-clients/profile) — the inbox that receives the agent's account-verification mail
* [Tenant users](/getting-started/users) — the identity that owns the browser, email, cards, and vault
