Create a vault session
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.
string
Your own id for the user, 1 to 128 characters, such as
cust_48213. Agentcard echoes it on vault.session_linked and on the session read, so you match the user to your records without a lookup table. Send the same id on every link you mint for the same user.string[]
Pass
["card"] to open the card form even when the user already has a card on file. Use it for an “add another card” link. Without it, a user with a card on file sees that their card is ready and goes back to your app.number
Lifetime in seconds (60 to 172,800). Defaults to 24 hours. Each session is single use.
$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 (nouser_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
Deliverurl 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.
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.external_user_id of a user who already has a vault, the page skips the card form and asks for that user’s passkey. The session links to the same user_id you already hold, even if the user signed out of the Vault first. Another person’s passkey cannot finish the link, and nobody can set up a second vault on it.
Let a user add another card
Mint a link with the user’sexternal_user_id and steps: ["card"], then send it the same way:
user_id you already hold, and you get vault.card_stored for it. List the user’s cards to see both:
Learn when the card is stored
You have two options.Option A: webhooks (recommended)
vault.session_linked
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 theid from the create response, never the token inside url.
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: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 →
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, so the purchase flow works against test-mode storefronts.