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

# Install the SDK

> Install @agentvault/playwright, configure it, and run your first proxied checkout.

Install the Playwright SDK and run a checkout where your code only ever handles
mock tokens.

## Overview

`@agentvault/playwright` is a drop-in for Playwright's `chromium`. Every browser
action runs in the Agent Vault proxy. Your agent fills forms with
[mock tokens](/reference/glossary), and the proxy substitutes the real card
numbers, addresses and passwords inside its own browser.

| Requirement | Detail |
| - | - |
| Runtime | Bun, or Node 20 or later. On Node below 22, which has no global `WebSocket`, the SDK uses the `ws` package automatically |
| Environment | Server side only. It uses Node and Bun globals such as `process.env` and `Buffer`, so it does not run in a browser |
| Dependencies | `playwright-core` and `ws`, both public. No access to any private registry or repository is needed |

<Tip>
  Prefer no code at all? The [MCP server](/mcp/setup) exposes the same browser as
  tools for Claude Code, Claude Desktop, Cursor and Codex.
</Tip>

## The install URL

The package is not on npm. Agent Vault hosts it as a self-contained tarball:

```text theme={null}
https://app.agentvault.io/sdk/agentvault-playwright.tgz
```

<Note>
  Self-hosting Agent Vault? Copy the whole block from the framework picker on your
  dashboard's **Overview** page instead: it fills in your deployment's install URL,
  proxy URL and API URL. Changing only the install URL sends your key to the hosted
  proxy and API, which do not recognise it.
</Note>

<Warning>
  Only use an `http://` install URL for `localhost` or `127.0.0.1`. The package
  runs on your machine, and anyone on the network path could replace it.
</Warning>

## Install

<Steps>
  <Step title="Add the package">
    <CodeGroup>
      ```bash bun theme={null}
      bun add https://app.agentvault.io/sdk/agentvault-playwright.tgz
      ```

      ```bash npm theme={null}
      npm install https://app.agentvault.io/sdk/agentvault-playwright.tgz
      ```
    </CodeGroup>

    The URL has no version number, so it never changes. Each deployment serves its
    current build, and the version is in the installed package's `package.json`. Run
    the install again to upgrade.
  </Step>

  <Step title="Set the environment variables">
    ```bash theme={null}
    export SELF_AGENT_PAY_API_KEY=av_...                               # from the dashboard
    export SELF_AGENT_PAY_PROXY_URL=wss://proxy.agentvault.io/v1             # the proxy
    export SELF_AGENT_PAY_API_URL=https://api.agentvault.io  # the token catalog API
    ```

    All three are needed:

    * Without the API key, `listTokens()` and `chromium.launch()` throw `no API key`.
    * Without the proxy URL, the SDK targets a local proxy at `ws://localhost:4000/v1`
      and `chromium.launch()` fails with `Failed to connect to proxy`.
    * Without the API URL, `listTokens()` fails.

    A process that serves several keys can instead pass `apiKey` and `proxyUrl` to
    `chromium.launch()`, and `apiKey` and `baseUrl` to `listTokens()`. See
    [Launching](/sdk/api-reference#launching). The full list is on
    [Environment variables](/reference/environment-variables).
  </Step>

  <Step title="Check the catalog">
    The snippet uses top-level `await`: save it as `check.mjs`, or set
    `"type": "module"` in your `package.json`.

    ```ts theme={null}
    import { listTokens } from "@agentvault/playwright";

    const catalog = await listTokens();
    console.log(catalog.cards.map((c) => `${c.brand} ${c.last4}`));
    ```

    This needs no browser. If it throws `invalid_api_key`, check the key on the
    dashboard's **API keys** page.
  </Step>
</Steps>

## Run a checkout

```ts theme={null}
import { chromium, defaultIdentity, listTokens } from "@agentvault/playwright";

// 1. Discover the tokens this key may use. The catalog never contains a real
//    card number or CVV: only tokens, plus brand, last4 and the real expiry.
const catalog = await listTokens();
const me = defaultIdentity(catalog)!;
const ship = catalog.addresses.find((a) => a.label === "ship") ?? catalog.addresses[0];
const card = catalog.cards[0];
// A field the account holder has not filled in is listed in `missing`; its token
// throws token_not_found. Ask for it before you start.
const needed = ["first_name", "last_name", "email"].filter((f) => me.missing?.includes(f));
if (needed.length) throw new Error(`Ask the user to add to their profile: ${needed.join(", ")}`);

// 2. Launch a browser through the proxy.
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("https://shop.example.com/checkout");

// 3. Orient now. After the first token fill, snapshots are refused
//    (sensitive_data_blocked).
console.log(await page.snapshot());

// 4. Fill with tokens exactly as the catalog gives them.
await page.fill("#firstName", me.tokens.first_name);
await page.fill("#lastName", me.tokens.last_name);
await page.fill("#email", me.tokens.email);
await page.fill("#street", ship.tokens.street1);
await page.fill("#city", ship.tokens.city);
await page.fill("#state", ship.tokens.state);
await page.fill("#zip", ship.tokens.zip);

// 5. Expiry is a real value, not a token. Fill it before the number.
await page.fill("#expiryMonth", String(card.expiryMonth));
await page.fill("#expiryYear", String(card.expiryYear));

// 6. Number, then security code, with nothing in between. Pass the order
//    total from the page's order summary on the number fill, so the account
//    holder's approval threshold is checked against it.
const orderTotal = 49.99; // placeholder: use the total on the page
await page.fill("#cardNumber", card.number, { amount: orderTotal, currency: "USD" });
await page.fill("#cvv", card.cvv);

await browser.close();
```

The proxy resolves each token at fill time, so the merchant receives a real card
while your process never handles the card number or security code. It does see
the real expiry, brand and last 4 digits.

<Note>
  The card order in step 6 matters. Filling the number authorizes the security
  code that follows it exactly once, and any action in between asks the account
  holder to approve the purchase a second time. See [Card order](/mcp/tool-reference#card-order).
</Note>

## Next steps

<CardGroup cols={2}>
  <Card title="API reference" icon="code" href="/sdk/api-reference">
    Exports, the browser object model and the catalog helpers.
  </Card>

  <Card title="Sessions and timeouts" icon="clock" href="/sdk/sessions-and-timeouts">
    Which errors end a session and which do not.
  </Card>

  <Card title="Error codes" icon="triangle-exclamation" href="/sdk/error-codes">
    What each refusal means and whether to retry.
  </Card>

  <Card title="First checkout" icon="cart-shopping" href="/guides/first-checkout">
    A guided checkout, end to end.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.