> ## 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.

# Add the Vault to a web app

> Open the Vault's pages from your web app in a new tab, and drop the wallet sheet into your page with one script tag.

Your web app can use the Vault in two ways, and they combine. Open the links directly: the add-a-card page and the approval page open in a new tab, with nothing to install. Or add the JavaScript SDK: a sheet opens over your page, shows the user their cards, and adds a card through the Vault when they ask.

## Open the Vault from your page

Create a vault session on your server, then open its `url` from a click in the page.

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/vault_sessions \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

```html theme={null}
<button id="add-card">Add a card</button>
<script>
  document.getElementById("add-card").addEventListener("click", async () => {
    const tab = window.open("", "_blank");   // open on the click itself, before any await
    const session = await fetch("/api/agentcard/vault-session", { method: "POST" }).then((r) => r.json());
    tab.location = session.url;
  });
</script>
```

Open the tab on the click itself. A tab opened after a network call counts as a popup, and Safari and Firefox block it. Never load the Vault in an iframe: the page refuses to render inside another page's frame, and the user sees a blank box.

Open the approval link the same way. When your agent reaches the payment form, `onApprovalUrl` fires with a link; show the user a button that opens it in a new tab, or send it to them by push notification or email if they are not looking at your page.

When the user finishes, you receive `vault.session_linked` with their `user_id`, then `vault.card_stored`. Store the `user_id`: it is what you pass as `user` on every checkout. Without webhooks, [poll the session](/vault/adding-a-card#option-b-poll-the-session) instead.

## Show the wallet in your page

The JavaScript SDK opens a sheet over your page. Inside it, the user sees the cards in their vault, and "Add a card" opens the Vault in a new tab. Card numbers never enter your page. The sheet needs a wallet link, and a wallet link needs a user who has connected to your platform, so the setup has four steps.

### 1. Register your origins

The sheet only opens on origins you have registered. Register them once from your server:

```bash theme={null}
curl -X PUT https://api.agentcard.sh/api/v2/embed_origins \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"origins": ["https://app.example.com", "http://localhost:3000"]}'
```

```json theme={null}
{ "object": "embed_origins", "origins": ["https://app.example.com", "http://localhost:3000"] }
```

`PUT` replaces the whole list and `GET` reads it back. You can register up to 10 origins, `https` only, except that `http` is allowed for `localhost`, `127.0.0.1` and `[::1]` while you develop. A change takes effect within about five minutes. On an origin you have not registered, the sheet renders nothing, so this is the first thing to check when you see a blank frame.

### 2. Connect the user

A wallet link is issued to a connected user. Send the user a one-time code, verify it, and record their consent. The `user.id` you get back is the same id the Vault uses.

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/connect/start \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com"}'
```

Then [verify the code](/api-reference/connections/verify) and [record consent](/api-reference/connections/consent) for the returned `user.id`. In test mode the code is always `111111`.

### 3. Create a wallet link

```bash theme={null}
curl -X POST https://api.agentcard.sh/api/v2/wallet_links \
  -H "Authorization: Bearer $ORG_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"user_id": "usr_8f3k2m"}'
```

```json theme={null}
{
  "object": "wallet_link",
  "id": "wl_7h2k9m",
  "user_id": "usr_8f3k2m",
  "status": "active",
  "url": "https://app.agentcard.sh/w/wl_7h2k9m.4f8s…",
  "expires_at": "2026-09-15T18:15:00Z",
  "test_mode": false
}
```

Hand `url` to your page. A link lasts 15 minutes by default; pass `expires_in` (60 to 86400 seconds) to change that. Create a fresh link each time the wallet opens rather than storing one.

### 4. Open the sheet

Load the script from Agentcard, fetch the wallet link from your own server, and open the sheet:

```html theme={null}
<script src="https://app.agentcard.sh/js/v1/agentcard.js"></script>
<script>
  async function openWallet() {
    const link = await fetch("/api/agentcard/wallet-link", { method: "POST" }).then((r) => r.json());

    const wallet = AgentCard.create({
      token: link.url,
      onSuccess: (data) => {
        // data.type is "card_attached" (with data.cards) or "payment_completed"
      },
      onTokenExpired: async () => {
        const fresh = await fetch("/api/agentcard/wallet-link", { method: "POST" }).then((r) => r.json());
        wallet.update({ token: fresh.url });
      },
    });

    wallet.open();               // a sheet over your page
    // or: wallet.mount("#wallet");   render it inline instead
  }
</script>
```

Load the script from Agentcard's URL rather than bundling it, so fixes reach your users without a deploy on your side. Do not wrap the sheet in an iframe of your own: the SDK manages its frame and listens for its events, and a frame in between swallows them.

When the user taps "Add a card", the sheet opens the Vault in a new tab. If the browser blocks that tab, the sheet shows a "Continue" button the user can tap instead. Pass `onOpenUrl(url)` to take over how that link opens.

The other methods: `update(next)` swaps the token in place, `exit()` closes the sheet, and `destroy()` removes everything. Pass `theme: "light"` to match a light page, and `onEvent(name, data)` to receive every event for analytics.

## Confirm with webhooks

`onSuccess` tells your page what the user just did. Your server should act on webhooks, because the sheet's event can fire on a page the user then closes:

* `vault.card_stored` when a card lands in the vault.
* `checkout_authorization.approved` and the other `checkout_authorization.*` events when purchases are approved or declined.

## Test it

A sandbox token creates a sandbox session, a sandbox connection and a sandbox wallet link. The connect code is always `111111`. Store any of [Stripe's published test cards](https://docs.stripe.com/testing), any future expiry, any CVC. In test mode, "Add a card" inside the sheet stores an instant test card and fires `card_attached` without opening the Vault; a live session opens the Vault. Rehearse a purchase against [shop.agentcard.sh](https://shop.agentcard.sh), a demo store on Stripe test mode.

Next: [Create a cart](/vault/creating-a-cart).
