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

# Creating a cart

> Attach Agentcard to the browser your agent already uses, then let it shop.

Your agent builds the cart the way it already does: in a real browser, on the merchant's own website. Agentcard does not change how the agent shops. It attaches to the browser and waits for the moment the checkout form sends the card to the payment processor.

There is one rule: **attach before the agent reaches the payment form.** The SDK has to be watching when the page tries to send the card.

## Choose a browser

The SDK attaches to any Playwright Chromium page, which means it works with:

* **[KERNEL](https://kernel.so)**: connect over `cdp_ws_url`. KERNEL also ships a [native Agentcard integration](https://www.kernel.sh/docs/integrations/payments/agentcard) that needs no SDK. Pick one per checkout.
* **[Browserbase](https://www.browserbase.com)**: connect over the session's `connectUrl`.
* **Any CDP browser**: supply its CDP URL.

Your agent keeps control of the browser during approval and afterward.

## Install

```bash theme={null}
npm i @agent-cards/checkout@0.3.0 playwright-core
# plus your browser provider, e.g. @onkernel/sdk
```

You need your Agentcard `client_id` and `client_secret` on your server, and the `user_id` of the person whose card will pay (from [Adding a card](/vault/adding-a-card)).

## Attach to the page

```ts theme={null}
import { chromium } from 'playwright-core';
import { VaultClient, attachToPlaywright } from '@agent-cards/checkout';

const vault = new VaultClient({
  clientId: process.env.AGENTCARD_CLIENT_ID!,
  clientSecret: process.env.AGENTCARD_CLIENT_SECRET!,
});
await vault.syncRegistry(); // pulls the current processor list; safe on every run

const browser = await chromium.connectOverCDP(CDP_URL);
const context = browser.contexts()[0] ?? (await browser.newContext({ serviceWorkers: 'block' }));
const page = context.pages()[0] ?? (await context.newPage());

const checkout = await attachToPlaywright(page, {
  vault,
  user: 'usr_8f3k2m',
  merchant: 'shop.example.com',
  amountCents: 2306,
  currency: 'usd',
  onApprovalUrl: (url) => sendToUser(url),
});

// Now let the agent shop.
await page.goto('https://shop.example.com');
await runAgentShopping(page);
```

<ParamField body="user" type="string" required>
  The Agentcard user whose vaulted card pays.
</ParamField>

<ParamField body="merchant" type="string" required>
  Shown on the approval screen. Pass what the user would recognize.
</ParamField>

<ParamField body="amountCents" type="number">
  What the user approves, as an integer in the smallest unit (2306 for \$23.06). Pass with `currency`. On a Stripe PaymentIntent confirm, Agentcard holds the processor to this amount. Elsewhere it is shown and reported.
</ParamField>

<ParamField body="currency" type="string">
  ISO 4217 code for `amountCents`. Required with it.
</ParamField>

<ParamField body="onApprovalUrl" type="function">
  Called with the approval link the moment the payment pauses. Deliver it to the user through your own channel.
</ParamField>

## Two things that break attachment

* **Service workers.** Use a context with `serviceWorkers: 'block'`. Playwright cannot intercept requests from an already-active service worker, so the SDK refuses to attach to one.
* **Attaching too late.** If the agent has already submitted the payment form, there is nothing to pause. Attach right after you get the page, before navigation.

## Don't want a browser?

Agentcard's [Purchase API](/vault/integrations/ecommerce-apis/purchase-api) builds the cart and completes the checkout for you on the merchants it covers (Amazon, Walmart, Target, DoorDash, and more). No browser to run, same Face ID approval for the user.
