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

# Adding a card to the Vault

> Create a vault session, send the user one link, and get back a user id you can charge against.

A user adds a card to the Vault once. After that, any agent you run can pay with it, and the user only needs to approve each purchase.

You don't build a card form. Instead, you create a **vault session**, send the user its link, and Agentcard handles the rest: the page, the passkey, and the encryption.

The page carries your branding. Set a display name and upload a logo in the dashboard under **Settings → General → Branding**, and every link you mint shows them in the header and tab title. The address, the passkey, and the "Powered by Agentcard" footer stay Agentcard's; for a fully dedicated domain, [talk to us](mailto:karen@agentcard.sh).

## Create a vault session

```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 '{}'
```

```json theme={null}
{
  "object": "vault_session",
  "id": "vs_2q9d1x8f3k2m4t7w",
  "user_id": null,
  "url": "https://vault.agentcard.sh/v?vs=vs_2q9d1x8f3k2m4t7w.3k1v…",
  "expires_at": "2026-08-28T21:00:00Z",
  "poll_interval": 3,
  "test_mode": false
}
```

<ParamField body="user_id" type="string">
  Omit this for a new user. The session is **open**: Agentcard creates the user during enrollment and returns their id. If you pass an id you already have, the session is **connected**: opening the link sends a one-time code to the contact on that account, and verifying it signs the user in.
</ParamField>

<ParamField body="expires_in" type="number">
  Lifetime in seconds (60 to 172,800). Defaults to 24 hours. Each session is single use.
</ParamField>

`$ORG_TOKEN` must be a client-credentials access token. API keys are rejected with `400 client_credentials_required`. Open sessions are limited to 200 per organization per rolling 24 hours.

### Which kind of session

Default to an **open** session (no `user_id`). The user sees the card form straight away, with no account to create and no code to type, and a returning user picks "Unlock with your passkey" on the same page. Pass `user_id` only when you already hold one and want the link bound to that account before the user opens it; that **connected** session starts with a one-time code to the contact on the account, which is a sign-in step your user has to get through first.

## Send the link

Deliver `url` to the user in the thread or app you already use with them. Each link is for a single enrollment, so send it to one person and associate the resulting `user_id` with whoever you sent it to.

<Note>
  Send the URL untouched: on its own line, as the last thing in the message, or as a message of its own. The token after `vs=` is signed, and any character glued onto it (a period, a closing bracket, the first word of the next sentence) makes the phone open a different link. That link fails verification and the Vault shows its **Sign in** page instead of the card form, so your user sees a login step that was never part of the flow. Agents that compose messages with a language model get this wrong easily; have your code send the link rather than the model.
</Note>

What the user sees: a page where they enter their card details and save. A passkey locks the card, with fingerprint or face unlock on Android and Face ID or Touch ID on iPhone and Mac, and the card is encrypted on their device before it's stored. A master password is offered afterwards as a backup. The page asks for a master password before the card is stored when the card will open only in this browser. No account to create first, and no code to enter.

**Returning users** choose "Unlock with your passkey" instead of entering a card. The passkey signs them in, and the session links to their existing account.

## Learn when the card is stored

You have two options.

### Option A: webhooks (recommended)

| Event | When |
| - | - |
| `vault.session_linked` | The session got its user. Carries the `user_id` to store. |
| `vault.card_stored` | A card landed in the vault. Carries `card_id`, `brand`, `last4`. |

```json vault.session_linked theme={null}
{
  "type": "vault.session_linked",
  "data": {
    "vault_session_id": "vs_2q9d1x8f3k2m4t7w",
    "user_id": "usr_8f3k2m"
  }
}
```

A connected session already named its user, so it does not send `vault.session_linked`. It still sends `vault.card_stored`.

### Option B: poll the session

For a CLI or an agent with no public endpoint, read the session until it finishes. Use the `id` from the create response, never the token inside `url`.

```bash theme={null}
curl https://api.agentcard.sh/api/v2/vault_sessions/vs_2q9d1x8f3k2m4t7w \
  -H "Authorization: Bearer $ORG_TOKEN"
```

| `status` | Meaning | What to do |
| - | - | - |
| `pending` | The user has not finished yet. | Wait `poll_interval` seconds, read again. |
| `linked` | Done. `user_id` is set. | Store it. Stop polling. |
| `expired` | The link died unused. | Create a new session. |

Honor `poll_interval` and you will never hit the read budget (40 reads a minute per session).

## Check for an existing card

Never send a returning user through enrollment twice. Ask what they already have first:

```bash theme={null}
curl "https://api.agentcard.sh/api/v2/vault_cards?user_id=usr_8f3k2m" \
  -H "Authorization: Bearer $ORG_TOKEN"
```

Display fields only: `id`, `brand`, `last4`, expiry. Never card data. Pass a card's `id` as `cardId` on a checkout when the user should pay with a specific card.

[Check for stored cards →](/vault/checking-for-stored-cards)

## Test mode

A sandbox token creates a sandbox session. The passkey ceremony is real in the browser, and the user it creates is a sandbox user. On a connected session no code is delivered; `111111` verifies. Store a test card, any of [Stripe's published test cards](https://docs.stripe.com/testing), so the purchase flow works against test-mode storefronts.


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