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

# Quickstart

> Create Evervault Browser Tokens and use them in a Browserbase session.

This guide connects Evervault's [Card component](https://docs.evervault.com/cards/card-collection) to a Browserbase session. The browser automation receives token values; Evervault reveals the underlying card data only to the hostnames in the token policy.

## 1. Collect encrypted card data

Load the Evervault JavaScript SDK directly from its CDN. The Card component runs in an Evervault-hosted iframe, so PAN and CVC are encrypted before they reach your parent page or backend.

```html index.html theme={null}
<script src="https://js.evervault.com/v2"></script>
<div id="card-details"></div>
<button id="save-card" type="button" disabled>Save card</button>

<script>
  const evervault = new Evervault("<TEAM_ID>", "<APP_ID>");
  const card = evervault.ui.card({
    theme: evervault.ui.themes.clean(),
  });
  const saveButton = document.querySelector("#save-card");
  let cardState;

  card.mount("#card-details");
  card.on("change", (data) => {
    cardState = data;
    saveButton.disabled = !data.isComplete;
  });

  saveButton.addEventListener("click", async () => {
    const { number, expiry, cvc } = card.values.card;
    if (
      !cardState?.isComplete ||
      !number?.startsWith("ev:") ||
      !cvc?.startsWith("ev:")
    ) {
      throw new Error("Enter a complete card before continuing");
    }

    const { month: expiryMonth, year: expiryYear } = expiry;
    const response = await fetch("/api/cards", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        number,
        cvc,
        expiryMonth,
        expiryYear,
      }),
    });

    if (!response.ok) {
      throw new Error(`Card submission failed: ${response.status}`);
    }
  });
</script>
```

Your `/api/cards` endpoint should accept Evervault-encrypted `number` and `cvc` values plus expiry month/year metadata. Allow only those expected fields, reject plaintext or malformed ciphertext, and validate the expiry metadata server-side. Client-side validation is not a security boundary.

Store the encrypted PAN only under your reviewed retention policy. Treat CVC as transaction-specific input: do not retain it after authorization, even encrypted. Do not decrypt PAN or CVC in your automation backend or log submitted values.

## 2. Create Browser Tokens

For each checkout, send the `ev:...` ciphertext returned by Card Collection (`card.values.card.number` and `card.values.card.cvc`) to your backend. Immediately before starting automation, send those same ciphertext strings as `data[].value` to Evervault's `POST /browser-tokens` API. You do not need to decrypt or re-encrypt them first.

Evervault returns fresh, short-lived, format-preserving PAN and CVC placeholders plus separate proxy credentials. Browserbase fills the placeholders, not the ciphertext or original values. The reveal policy controls disclosure to a destination, not authorization to charge the card. For later transactions, you may reuse a stored encrypted PAN under your reviewed retention policy, but collect CVC again if required.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { Browserbase } from "@browserbasehq/sdk";

  const bb = new Browserbase({
    apiKey: process.env.BROWSERBASE_API_KEY!,
  });

  type CheckoutCard = {
    number: string;
    cvc: string;
    expiryMonth: string;
    expiryYear: string;
  };

  export async function createCheckoutSession(card: CheckoutCard) {
    const numberId = `card-number-${crypto.randomUUID()}`;
    const cvcId = `card-cvc-${crypto.randomUUID()}`;
    const tokenResponse = await fetch("https://api.evervault.com/browser-tokens", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization:
          "Basic " +
          Buffer.from(
            `${process.env.EVERVAULT_APP_ID}:${process.env.EVERVAULT_API_KEY}`,
          ).toString("base64"),
      },
      body: JSON.stringify({
        data: [
          { category: "card.number", value: card.number, id: numberId },
          { category: "card.cvv", value: card.cvc, id: cvcId },
        ],
        ttl: 300,
        permissions: [
          { action: "reveal", target: "www.example-merchant.com" },
          { action: "reveal", target: "api.payment-gateway.com" },
        ],
      }),
    });

    if (!tokenResponse.ok) {
      throw new Error(`Evervault returned ${tokenResponse.status}`);
    }

    const tokens = await tokenResponse.json();
    const numberToken = tokens.data.find((item: { id: string }) => item.id === numberId);
    const cvcToken = tokens.data.find((item: { id: string }) => item.id === cvcId);

    if (!numberToken || !cvcToken) {
      throw new Error("Evervault did not return both card tokens");
    }

    return {
      tokens,
      numberToken: numberToken.value,
      cvcToken: cvcToken.value,
      expiryMonth: card.expiryMonth,
      expiryYear: card.expiryYear,
    };
  }
  ```

  ```python Python theme={null}
  import os
  from uuid import uuid4

  import requests
  from browserbase import Browserbase

  bb = Browserbase(api_key=os.environ["BROWSERBASE_API_KEY"])


  def create_checkout_session(card: dict[str, str]):
      number_id = f"card-number-{uuid4()}"
      cvc_id = f"card-cvc-{uuid4()}"
      response = requests.post(
          "https://api.evervault.com/browser-tokens",
          auth=(os.environ["EVERVAULT_APP_ID"], os.environ["EVERVAULT_API_KEY"]),
          json={
              "data": [
                  {"category": "card.number", "value": card["number"], "id": number_id},
                  {"category": "card.cvv", "value": card["cvc"], "id": cvc_id},
              ],
              "ttl": 300,
              "permissions": [
                  {"action": "reveal", "target": "www.example-merchant.com"},
                  {"action": "reveal", "target": "api.payment-gateway.com"},
              ],
          },
          timeout=30,
      )
      response.raise_for_status()
      tokens = response.json()
      number_token = next((item for item in tokens["data"] if item["id"] == number_id), None)
      cvc_token = next((item for item in tokens["data"] if item["id"] == cvc_id), None)

      if not number_token or not cvc_token:
          raise RuntimeError("Evervault did not return both card tokens")

      return {
          "tokens": tokens,
          "number_token": number_token["value"],
          "cvc_token": cvc_token["value"],
          "expiry_month": card["expiryMonth"],
          "expiry_year": card["expiryYear"],
      }
  ```
</CodeGroup>

The token response also contains `proxyConfiguration.hostname`, `port`, `username`, and `password`. Keep those credentials server-side and use them only to create the matching session.

## 3. Trust the Evervault CA

Evervault's proxy replaces token values in HTTPS requests. Download the [Evervault CA certificate](https://ca.evervault.com), save it as a PEM file, and upload it to the Browserbase project once:

<CodeGroup>
  ```typescript Node.js theme={null}
  import { readFile } from "node:fs/promises";
  import { toFile } from "@browserbasehq/sdk";

  const certificate = await bb.certificates.create({
    file: await toFile(
      await readFile("./evervault-ca.pem"),
      "evervault-ca.pem",
    ),
  });

  console.log("Store this ID in your secret configuration:", certificate.id);
  ```

  ```python Python theme={null}
  with open("./evervault-ca.pem", "rb") as certificate_file:
      certificate = bb.certificates.create(file=certificate_file)

  print("Store this ID in your secret configuration:", certificate.id)
  ```
</CodeGroup>

The certificate ID belongs to the Browserbase project associated with your API key. Reuse it for later sessions instead of uploading it for every task.

## 4. Launch Browserbase through Evervault

Pass the returned proxy credentials as an external Browserbase proxy and reference the uploaded CA certificate. The example disables Browserbase logs and recordings, keeps certificate validation enabled, and uses a bounded session timeout. Replace the expiry and confirmation selectors with the merchant's actual fields and success state.

<CodeGroup>
  ```typescript Node.js theme={null}
  import { chromium, type Browser } from "playwright-core";

  export async function runCheckout(
    proxyConfiguration: {
      hostname: string;
      port: number;
      username: string;
      password: string;
    },
    numberToken: string,
    cvcToken: string,
    expiryMonth: string,
    expiryYear: string,
  ) {
    const session = await bb.sessions.create({
      api_timeout: 240,
      browserSettings: {
        recordSession: false,
        logSession: false,
        ignoreCertificateErrors: false,
      },
      proxies: [
        {
          type: "external",
          server: `https://${proxyConfiguration.hostname}:${proxyConfiguration.port}`,
          username: proxyConfiguration.username,
          password: proxyConfiguration.password,
        },
      ],
      proxySettings: {
        caCertificates: [process.env.EVERVAULT_CA_CERTIFICATE_ID!],
      },
    });

    let browser: Browser | undefined;
    try {
      browser = await chromium.connectOverCDP(session.connectUrl);
      const page = browser.contexts()[0].pages()[0];
      await page.goto("https://www.example-merchant.com/checkout");
      await page.fill("[name='cardnumber']", numberToken);
      await page.fill("[name='exp-month']", expiryMonth);
      await page.fill("[name='exp-year']", expiryYear);
      await page.fill("[name='cvc']", cvcToken);
      await page.click("button[type='submit']");
      await page.locator("[data-checkout-status='approved']").waitFor();
      return session.id;
    } finally {
      try {
        await browser?.close();
      } finally {
        await bb.sessions.update(session.id, { status: "REQUEST_RELEASE" });
      }
    }
  }
  ```

  ```python Python theme={null}
  import os

  from playwright.sync_api import Browser, sync_playwright


  def run_checkout(
      proxy_configuration: dict[str, str | int],
      number_token: str,
      cvc_token: str,
      expiry_month: str,
      expiry_year: str,
  ) -> str:
      session = bb.sessions.create(
          api_timeout=240,
          browser_settings={
              "record_session": False,
              "log_session": False,
              "ignore_certificate_errors": False,
          },
          proxies=[
              {
                  "type": "external",
                  "server": (
                      f"https://{proxy_configuration['hostname']}:"
                      f"{proxy_configuration['port']}"
                  ),
                  "username": proxy_configuration["username"],
                  "password": proxy_configuration["password"],
              },
          ],
          proxy_settings={
              "ca_certificates": [os.environ["EVERVAULT_CA_CERTIFICATE_ID"]],
          },
      )

      try:
          with sync_playwright() as playwright:
              browser: Browser | None = None
              try:
                  browser = playwright.chromium.connect_over_cdp(session.connect_url)
                  page = browser.contexts[0].pages[0]
                  page.goto("https://www.example-merchant.com/checkout")
                  page.locator("[name='cardnumber']").fill(number_token)
                  page.locator("[name='exp-month']").fill(expiry_month)
                  page.locator("[name='exp-year']").fill(expiry_year)
                  page.locator("[name='cvc']").fill(cvc_token)
                  page.locator("button[type='submit']").click()
                  page.locator("[data-checkout-status='approved']").wait_for()
                  return session.id
              finally:
                  if browser:
                      browser.close()
      finally:
          bb.sessions.update(session.id, status="REQUEST_RELEASE")
  ```
</CodeGroup>

Use the token values returned by `createCheckoutSession` or `create_checkout_session`; never fetch the original card values for this step. Evervault reveals the originals only when the request goes through its proxy, matches an allowed hostname, and occurs before the token expires. After authorization, discard the CVC token, ciphertext, and matching proxy credentials.

## Troubleshooting

### Certificate errors

If HTTPS pages fail with `ERR_CERT_AUTHORITY_INVALID`, verify that the Evervault CA is a valid PEM certificate, that it is uploaded to the same Browserbase project as the API key, and that its ID is present in `proxySettings.caCertificates`. Do not set `ignoreCertificateErrors`.

### The checkout rejects a token

Confirm that the token category matches the field (`card.number` or `card.cvv`), that the token has not expired, and that the browser request reaches a hostname in `permissions`. Include the PSP or iframe hostname when the merchant does not process card data directly.

## Production checklist

* Store an Evervault-encrypted PAN only under your reviewed retention policy. Do not retain CVC after authorization, even encrypted.
* Create tokens immediately before a checkout and set the shortest workable TTL.
* Allowlist exact merchant and PSP hostnames.
* Keep token values and proxy credentials out of logs, prompts, screenshots, and recordings where possible.
* Use a project-scoped Browserbase CA certificate and keep certificate IDs in configuration.
* Dispose of token values and proxy credentials after the session ends.
* Validate the complete flow with your security and compliance teams.


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