# API reference Source: https://docs.agentcard.sh/companies/api/reference Every v2 endpoint on its own page — parameters, responses, and a live playground to test with your own credentials. Each endpoint in this reference has its own page with every parameter, every response field, and an interactive playground on the right — you can call the real API from your browser without writing a line of code. Base URL: `https://api.agentcard.sh` There is no separate sandbox host: whether a call runs in **sandbox** or **production** is decided by the credential you use, never by the URL. ## Authentication Every endpoint is called from your backend with a **platform access token** in the `Authorization` header: ``` Authorization: Bearer ``` You mint that token by exchanging your `client_id` + `client_secret` on [Create an access token](/companies/api/reference/create-access-token). Get your credentials in the dashboard under **Organization → Developer → Credentials** — a sandbox client mints sandbox tokens; a production client mints production tokens. ## Two tokens, two jobs The OAuth flow hands you two different tokens — don't mix them up: | Token | Where you get it | What it does | | ------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | **Platform access token** | [Create an access token](/companies/api/reference/create-access-token), from your `client_id` + `client_secret` | Authenticates **your platform**. Goes in the `Authorization` header of every endpoint in this reference. | | **Connection token** | [Verify the code](/companies/api/reference/connect-verify), after the user completes the one-time code | Acts **on behalf of that user**. Send it as the bearer token to the [MCP server](/tools/mcp) to create cards, check balances, and shop as them. | The connection token belongs to the user, not your platform — it never goes in the `Authorization` header of these endpoints (they name the user with `user_id` instead), and it only sees what **your app** created for **that user**. Keep it fresh with [Refresh the connection](/companies/api/reference/connect-refresh). ## Test endpoints from this reference Open [Create an access token](/companies/api/reference/create-access-token), paste your `client_id` and `client_secret` into the playground, and hit **Send**. Use a sandbox client so nothing touches production. Copy the `access_token` from the response and paste it into the **Authorization** field on any endpoint page. It's remembered as you move between pages, and is only kept in your browser. Fill in the parameters and hit **Send** — you're hitting the live API. Tokens expire after one hour; mint a new one when calls start returning `401`. In sandbox, the connect code is always `111111`, so you can run the whole [Connect flow](/companies/api/reference/connect-start) — start → verify → consent → refresh — end to end from these pages. ## Errors Every v2 error uses the same envelope: ```json theme={null} { "error": { "code": "invalid_code", "message": "That code is invalid or expired.", "docs": "https://docs.agentcard.sh/companies/api/overview" } } ``` * `code` — a stable, machine-readable string (snake\_case). Branch on this. * `message` — a human-readable explanation, safe to log. * `docs` — a link back to the reference. Each endpoint page lists the codes it can return. Prefer Postman? The [ready-made collection](/companies/api/postman) runs both flows end to end. # Start a card attachment Source: https://docs.agentcard.sh/companies/api/reference/attach-start openapi.json POST /api/v2/cards/add Start attaching the connected user's own card. Returns a hosted `attach_url` for the user to open — adding the card takes about a minute (a one-time code from their bank, then a passkey). The card number is entered on the hosted page only; it never passes through your servers. Once attached, cards created over MCP charge this card directly — no identity verification and no wallet funding. ## How it works You ask for an attachment for a connected user; we return an `attach_url`. The user opens it and adds their own card in about a minute — a one-time code from their bank, then a passkey. The card number is entered on the hosted page only; it never passes through your servers. Poll [Get the attachment status](/companies/api/reference/attach-status) (or call this endpoint again — with an active attachment it answers `200` with the card instead of starting over). Once `active`, cards created over [MCP](/tools/mcp) charge the attached card directly — no identity verification, no wallet funding. ### Headless mode Pass `display: "headless"` to get an `attach_url` that renders just the checkout — no Agentcard logo, backdrop, or copy — for embedding the page in your own UI (for example a webview or modal you brand yourself). The flow and security are identical; only the chrome is removed, so your interface supplies the context around it. If the response is `422 user_info_required`, `missing_fields` names what to collect first: `phone_number` via [POST /api/v2/wallet/phone/start](/companies/api/reference/wallet-phone-start), `consent` via [POST /api/v2/connect/consent](/companies/api/reference/connect-consent). # Get the attachment status Source: https://docs.agentcard.sh/companies/api/reference/attach-status openapi.json GET /api/v2/cards/add The connected user's latest attachment: `pending` while the user hasn't finished the link, `active` with card details once attached, or `ineligible` with a reason when the card cannot be attached (fall back to wallet funding + `create_card`). ## Statuses * **`pending`** — the user hasn't finished the attach link. Nudge them to complete it; poll again after. * **`active`** — attached. `card` carries the display details (brand, last4 — never the full number). Cards created over [MCP](/tools/mcp) now charge this card directly. * **`ineligible`** — this card can't be attached; `reason` says why (for example `issuer_excluded`, `commercial_card`). Fall back to [wallet funding](/companies/api/reference/wallet-fund) and `create_card`. A `404 no_attachment` means nothing was ever started for that user — begin with [Start a card attachment](/companies/api/reference/attach-start). # Record consent Source: https://docs.agentcard.sh/companies/api/reference/connect-consent openapi.json POST /api/v2/connect/consent Records that the user authorized your platform to act on their behalf. Safe to retry — it's idempotent per user, so a repeat call updates the same record instead of creating a duplicate. ## What your terms must disclose Recording consent attests that your connect UI showed the user your terms for the Agentcard connection, and those terms must include this line (or equivalent): > By connecting, you also agree to [Crossmint's Privacy Policy](https://www.crossmint.com/legal/privacy-policy) — Crossmint may process payments when you add funds. Consents recorded through this endpoint make the connection eligible for the Crossmint funding rail on embedded links (Apple Pay / Google Pay / card). Connections without a recorded consent keep funding on the default rail. # Refresh the connection Source: https://docs.agentcard.sh/companies/api/reference/connect-refresh openapi.json POST /api/v2/connect/refresh Connection access tokens expire after one hour. Exchange the refresh token for a new pair before then. Each refresh returns a **new** refresh token and invalidates the old one — replace the stored token every time. # Send a code Source: https://docs.agentcard.sh/companies/api/reference/connect-start openapi.json POST /api/v2/connect/start Sends a one-time code to the user by email or phone. Provide **exactly one** of `email` or `phone`. Codes are valid for 10 minutes. If you've designed a connect email in your dashboard, we send that branded email; otherwise we send a default one. # Verify the code Source: https://docs.agentcard.sh/companies/api/reference/connect-verify openapi.json POST /api/v2/connect/verify Checks the code the user entered and, on success, connects the user and returns the token pair to store. Completing the code **is** the authorization — there is no separate approval screen. The returned `access_token` is the **user's connection token**: it acts on behalf of this user (send it as the bearer token to the MCP server to create cards, check balances, and shop as them). It is not the platform token — the endpoints in this reference keep using your platform access token and name the user with `user_id`. A code can be verified once: a second verify of the same attempt returns `invalid_connect_attempt`. In **sandbox** the code is always `111111`. The `access_token` returned here is the **user's connection token** — it acts on behalf of this user. Send it as the bearer token to the [MCP server](/tools/mcp) to create cards, check balances, and shop as them. It is **not** the platform token you send to the endpoints in this reference — those keep using your [platform access token](/companies/api/reference/create-access-token) and name the user with `user_id`. # Create an access token Source: https://docs.agentcard.sh/companies/api/reference/create-access-token openapi.json POST /api/v2/oauth/token Exchanges your `client_id` + `client_secret` for a platform access token (OAuth2 client credentials, RFC 6749 §4.4). The token lives one hour — when it expires, exchange again; there are no refresh tokens on this grant. Get your credentials in the Agentcard dashboard under **Organization → Developer → Credentials**. A sandbox client mints tokens that act in sandbox; a production client acts in production. You can also send the credentials as HTTP Basic (`Authorization: Basic base64(client_id:client_secret)`) instead of in the form body. This endpoint is rate limited to 30 requests per 5 minutes per IP — cache the token and reuse it until it expires. The exchange is also available at its original path, `POST /api/v1/oauth/token` — both addresses serve the same endpoint, and existing integrations do not need to change. New integrations should use `/api/v2/oauth/token`. **Test it right here** — paste your `client_id` and `client_secret` into the playground on the right and hit **Send**. Copy the `access_token` from the response into the **Authorization** field of any other endpoint page to call it live. A sandbox client runs everything in sandbox. # Introspect credential Source: https://docs.agentcard.sh/companies/api/reference/introspect openapi.json GET /api/v2 Returns the organization and mode your token acts as. Useful as a health check and to confirm you're pointed at the right credential. # Simulate an outcome (test mode) Source: https://docs.agentcard.sh/companies/api/reference/kyc-simulate openapi.json POST /api/v2/kyc/simulate **Test mode only.** Drives a test-mode verification to a chosen terminal outcome instantly — test verifications never complete on their own. The simulated verdict flows through the same status contract and fires the same `identity.verification.updated` webhook a real review produces, so your status handling and webhook consumer are exercised end to end. Requires a test-mode client credential; live tokens get `403 sandbox_only`. ## Why this exists Test-mode verifications never complete on their own — there is no reviewer behind them. This endpoint is the missing verdict: pick an outcome and the verification reaches it instantly, through the same status contract and the same `identity.verification.updated` webhook a real review produces. | Outcome | What your integration sees | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | `approved` | Status flips to `approved` — the happy path. | | `rejected` | Terminal `rejected` with a `reason` — the failure state to surface to the user. | | `requires_input` | A retryable bounce (`awaiting_documents` or `needs_information`, with a `reason`) — the "send new photos" loop your UI must handle. | Pass a custom `reason` to see exactly how your UI renders a specific review message. Re-simulating overwrites the previous outcome, so a single test user can walk approved → rejected → approved without reconnecting. Live tokens are refused with `403 sandbox_only`: live verifications are decided by the identity provider. # Get verification status Source: https://docs.agentcard.sh/companies/api/reference/kyc-status openapi.json GET /api/v2/kyc Polls the current verification status — the alternative to the `identity.verification.updated` webhook. ## Status values Every KYC response carries exactly one status. | Status | Meaning | What to do | | ----------------------- | ----------------------------------- | -------------------------------------------------------------------------------------- | | `awaiting_documents` | Waiting for the ID images. | Upload the front and back — or hand the user the `iframe_url`. | | `needs_information` | The review asked for typed details. | Submit the `required_fields` — or hand the user the `iframe_url`, which collects them. | | `requires_verification` | Ready for the face scan. | Show the user the `iframe_url`. | | `pending` | Under review. | Wait — no action needed. | | `approved` | Verified. | Done. | | `rejected` | Not verified. | The user did not pass. | ## Fully hosted, or fully custom Every **actionable** status (`awaiting_documents`, `needs_information`, `requires_verification`) carries an `iframe_url` — a hosted page that walks the user through whatever the verification still needs: document capture, any typed details the review asks for, and the face scan. That gives you two integration styles with the same API: * **Fully hosted**: ignore the upload and submit endpoints entirely. Whenever the status is actionable, hand the user the `iframe_url` (new tab or embedded) — the flow completes end to end on the hosted page, and no identity documents or personal details ever pass through your servers. * **Fully custom**: drive the [document upload](/companies/api/reference/kyc-upload-front) and [information submit](/companies/api/reference/kyc-submit-information) endpoints from your own UI, and use the `iframe_url` only at `requires_verification` for the scan. The link is short-lived — always surface the one from your freshest status read or [`identity.verification.updated`](/companies/webhooks#identity-verification-updated) event rather than storing it. # Submit information Source: https://docs.agentcard.sh/companies/api/reference/kyc-submit-information openapi.json POST /api/v2/kyc/information Submits the extra fields requested by a `needs_information` response. Send only the fields listed in `required_fields`. On success, the response returns the `iframe_url` for the face scan. # Upload the back of the ID Source: https://docs.agentcard.sh/companies/api/reference/kyc-upload-back openapi.json POST /api/v2/kyc/documents/back Uploads the back of the document. The response tells you what to do next — this is the branch point of the flow: - `needs_information` → collect exactly the `required_fields` and post them to `/kyc/information`. - `requires_verification` → show the user the `iframe_url` for the face scan. - `rejected` → the document couldn't be verified. # Upload the front of the ID Source: https://docs.agentcard.sh/companies/api/reference/kyc-upload-front openapi.json POST /api/v2/kyc/documents/front Uploads the front of the user's identity document as a base64-encoded image. This step acknowledges receipt; the next step (`back`) tells you what comes next. Any upload response may include a `warnings` array with actionable feedback (for example, that the other side of the document is still needed). The response's `extracted` object carries what the document reader pulled off the image — use it to prefill your details form so the user confirms instead of typing. # Create a connect session Source: https://docs.agentcard.sh/companies/api/reference/platform-connect-create openapi.json POST /api/v2/platform_connect_sessions Starts a hosted "Connect with Agentcard" flow for a company you're onboarding. Redirect the company to the returned `url`; when they finish, we redirect them to your `return_url` with a one-time `code` (plus your `state`) that you [exchange](/companies/api/reference/platform-connect-exchange) for the connected organization's API credentials. Requires client-credentials auth (your `client_id` + `client_secret`) and the platform capability on your organization — [contact us](mailto:support@agentcard.sh) to enable it. The `return_url` must exactly match a redirect URI registered on the OAuth client you authenticate with. # Exchange the code Source: https://docs.agentcard.sh/companies/api/reference/platform-connect-exchange openapi.json POST /api/v2/platform_connect_sessions/exchange Exchanges the one-time `code` from the return redirect for the connected organization's `client_id` + `client_secret`. The code alone resolves the session (standard OAuth token-endpoint shape) — no session id to track. Call it with the SAME platform credential that created the session, within 10 minutes of completion. One shot: the secret is returned only here; persist both before treating the exchange as done. `POST /api/v2/platform_connect_sessions/{session_id}/exchange` remains supported and behaves identically. # Get a connect session Source: https://docs.agentcard.sh/companies/api/reference/platform-connect-get openapi.json GET /api/v2/platform_connect_sessions/{session_id} Poll a session's status. `connected_organization_id` appears once the company completes the flow. The one-time code itself is never returned here — it only rides the redirect. # Create a funding session Source: https://docs.agentcard.sh/companies/api/reference/wallet-fund openapi.json POST /api/v2/wallet/fund Returns an Apple Pay / Google Pay payment link for the amount you specify. Show it in your UI; when the user completes the payment the funds land in their wallet. The link is single-use and expires after 30 minutes. Requires a phone verification fresh within 60 days — see `POST /api/v2/wallet/phone/start`. ## How it works Ask for a funding session for an amount; we return a `checkout_url`. There are two kinds, picked with `link_type`: * **`hosted`** (default): an Agentcard-hosted payment page. Render it anywhere — an "Add funds" button, a QR code, a chat message — and the user opens it and pays with Apple Pay, Google Pay, or card. Safe to relay: the underlying payment order is created only when the user opens the page. Creating a hosted session is free; an unopened link costs nothing. * **`embedded`**: an in-app payment link, minted immediately. Load `checkout_url` in a `WKWebView` (iOS) or Android WebView and the user pays without leaving your app. When the payment completes, the funds land in the user's wallet. Poll [`GET /api/v2/wallet/fund/{session_id}`](/companies/api/reference/wallet-fund-status) to know when — that endpoint is the source of truth for money; treat any client-side signal as advisory. ## Fees: send the exact amount **Agentcard covers the payment provider's fee.** Send `amount_cents` for exactly what the user should receive — the wallet is credited the full amount. Do **not** gross up the charge to compensate for fees; that just over-charges your user. * `fee_cents` on the create and status responses is the provider's fee (which Agentcard absorbs) when known, or `null` while it isn't yet — `null` means "unknown", never "free". * `fees_covered` tells you whether the fee is on us: `true` (it is — the user receives the full `amount_cents`), `false` (an anomalous fee we won't absorb — the wallet receives the net amount), or `null` (fee not known yet). `false` is rare; treat it as net delivery rather than failing the flow. ## Requirements * **The user must be identity-verified.** Funding reuses the user's completed identity verification — there is no separate verification step inside checkout. If the user isn't verified, create-session returns `422 kyc_required`; run identity verification first, then retry. Other verification-related 422s: * `funding_profile_required` — a one-time funding profile is missing (collect it, then retry). * `kyc_transfer_required` — the user's existing verification needs a one-time carry-over before funding (a single extra step, no re-verification). * `unsupported` — the user's verification can't be used for funding (for example, a non-US user). * **Amount bounds come from the API.** If the amount is out of range you get `422 amount_out_of_range` with `min_amount_cents` and `max_amount_cents` in the response — read those rather than hardcoding limits. * **Link lifetime.** `hosted` and `embedded` links both stay openable for the session's 30-minute window. ## Embedded: in-app checkout Create the session with `link_type: "embedded"`, hand `checkout_url` to your app, and load it in a WebView: ```swift theme={null} let webView = WKWebView(frame: .zero, configuration: WKWebViewConfiguration()) webView.load(URLRequest(url: checkoutUrl)) ``` * **Apple Pay** renders inside a `WKWebView` on **iOS 16+** (Apple Pay on the Web is supported in `WKWebView`, but **not** in `SFSafariViewController` — use a `WKWebView`). On older iOS the user pays with Google Pay or card. No domain verification is required on your side; the checkout runs on our Apple-registered domain and pops the native Apple Pay sheet in-app. * **Google Pay** works in a modern Android WebView; **card** is always available as a fallback. * **Completion:** the page navigates to **`/fund/success`** when the payment completes — observe that navigation to close your WebView — and the [status endpoint](/companies/api/reference/wallet-fund-status) is the authoritative confirmation. * **Wallet-only sheet:** append `&only=apple_pay` (or `&only=google_pay`) to the `checkout_url` fragment to present just that wallet button with no card form. ### Mint on demand, not per render Every embedded session creates a real payment order the moment you call the endpoint. Mint one only when the user has actually initiated payment (tapped "Add funds"), render it immediately, and create a fresh session for the next attempt if the link lapses. Don't mint on page render or in retry loops — the API refuses excess pending sessions for the same user with `too_many_pending_sessions`. * **Never relay an embedded link** through chat or email; link unfurlers can consume it and leave the user a dead page. Server → WebView, immediately, is the only safe path. * The embedded `checkout_url` appears only on the create response; the [status endpoint](/companies/api/reference/wallet-fund-status) never re-serves it. If it lapsed, create a new session. * **Test with sandbox-mode credentials** — sandbox clients create TEST orders (never charged), so you can exercise the full WebView flow end to end before switching to live keys. * On a physical device, cards must be in the platform wallet; simulators cannot show the Apple Pay sheet. # Get a funding session Source: https://docs.agentcard.sh/companies/api/reference/wallet-fund-status openapi.json GET /api/v2/wallet/fund/{session_id} Poll a funding session until it is `completed` — the payment status is refreshed from the provider on every read. ## Status values | Status | Meaning | What to do | | ------------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | `pending` | Waiting for the user to open the link and pay. | Hosted: show the `checkout_url`. Embedded: keep the webview link you already rendered. | | `processing` | Paid — the deposit is settling. | Wait; poll again. | | `completed` | Funds are in the wallet. | Done. | | `failed` | The payment failed (`failure_reason` says why). | Create a new session. | | `expired` | The link was never used within 30 minutes, or the payment was started and abandoned. | Create a new session. | `checkout_url` is present only while a hosted link can still be opened. Embedded sessions never carry `checkout_url` here — the link appears only on the create response; if it lapsed, create a new session. `expires_at` on this endpoint always reflects the session's 30-minute fundability window. ## Fees `fee_cents` is the payment provider's fee when known (`null` = unknown yet, never "free"). **Agentcard covers it** — the wallet is credited the full `amount_cents`; don't gross up. `fees_covered` reports the outcome: `true` (fee absorbed by Agentcard), `false` (rare anomalous fee — the wallet received the net amount), `null` (fee not known yet). # Get the user's wallet Source: https://docs.agentcard.sh/companies/api/reference/wallet-get openapi.json GET /api/v2/wallet The connected user's wallet and current balance — render it in your own wallet UI. Provisions the wallet on first read. ## Notes * The wallet is provisioned automatically on first read — there is no separate provision call. * `balance_usdc` is the current balance as a decimal string (e.g. `"25.00"`). * If `balance_unavailable` is present and `true`, the balance could not be read right now — treat it as "unknown", not "\$0". # Start phone verification Source: https://docs.agentcard.sh/companies/api/reference/wallet-phone-start openapi.json POST /api/v2/wallet/phone/start Sends the user a one-time code. Relay it through your UI — the user reads it back to you, same pattern as the connect code. A verification stays fresh for 60 days. Provide `phone_number` only when the user has no phone on file (any E.164 number outside the prohibited-countries list). ## How it works Some flows need the user's phone OTP-verified — for example, [adding a card](/companies/api/reference/attach-start) lists `phone_number` under `user_info_required`. Same embedded pattern as [connect](/companies/api/reference/connect-start): we send the code, your UI collects it, you [verify it](/companies/api/reference/wallet-phone-verify) server-to-server. * If the user already has a verified phone that's still fresh, the response is `already_verified` — nothing more to collect. * Pass `phone_number` only when the user has no phone on file. Any E.164 number works (e.g. `+14155550123`, `+4915123456789`) except numbers from [prohibited countries](https://agentcard.sh/prohibitions), which return `country_not_supported`. * A verification stays fresh for **60 days**; after that, run this flow again when a phone is required. # Verify the phone code Source: https://docs.agentcard.sh/companies/api/reference/wallet-phone-verify openapi.json POST /api/v2/wallet/phone/verify Checks the code the user read back. On success the verification stays fresh for 60 days and funding sessions can be created. ## Notes * `phone_number` is **required when the user had no phone on file at [start](/companies/api/reference/wallet-phone-start)** (e.g. a fresh connection before KYC) — the code was sent to the number you supplied, can only be checked against it, and there's no stored number to fall back on, so a call without it returns `phone_number_required`. Keep passing that **same** number on verify until the user is verified: the code is only ever valid against the number it was sent to, so don't switch to a stored number that shows up mid-flight (a KYC backfill between start and verify). Omit `phone_number` only when start sent the code to a number already on file. It's optional in the schema only because that stored-number case exists. * On `verified`, the user can fund immediately — the verification stays fresh for 60 days. * `invalid_code` carries a `reason` (`incorrect`, `expired`, `too_many_attempts`, or `no_code`) so you can decide between "try again" and "send a new code". # Request a withdrawal Source: https://docs.agentcard.sh/companies/api/reference/wallet-withdraw openapi.json POST /api/v2/wallet/withdrawals Requests a withdrawal from the user's spendable balance. Two rails: `bank` (default) pays a saved destination by wire; `address` sends USDC on Base to `destination_address`. Both are processed manually by the Agentcard team, usually within 1-3 business days; the user is emailed when the request is received and again when it is sent. Open (not yet completed or rejected) requests count against the balance, so a user cannot over-request. Amounts range from $2.00 to $10,000.00. ## How it works Requests a withdrawal from the user's spendable balance. Two rails: * **`bank`** (default) — pays a saved destination by ACH or international wire. Pass the `recipient_id` from [saved destinations](/companies/api/reference/wallet-withdrawal-recipients-list); if the user has none yet, [save one](/companies/api/reference/wallet-withdrawal-recipient-create) first. * **`address`** — sends USDC on Base to `destination_address` (a `0x` address the user provides). Agentcard-managed addresses are rejected (`internal_destination`). Both rails are **processed manually by the Agentcard team**, usually within 1-3 business days — set that expectation in your UI. The response comes back in `requested`; the status then walks `requested` → `processing` → `completed` (or `rejected`), and the user is emailed when the request is received and again when it is sent. * Amounts range from **$2.00 to $10,000.00** per request. * Open requests **hold the balance**: they count against the user's spendable balance until they complete or are rejected, so a user can never over-request. `insufficient_funds` returns `available_cents` — the balance net of holds — for a helpful retry message. * Only what the user can actually spend is withdrawable. Money your company allocated to a user under the company-funded flow is not — it stays yours, and you pull unused residuals back with a recovery (the `recover_funds` tool on [MCP](/tools/mcp)), never through a user withdrawal. * You can switch user withdrawals off for your whole organization from the dashboard (**Settings → General → User withdrawals**); while off, these endpoints return `withdrawals_disabled`. Track progress with [list withdrawals](/companies/api/reference/wallet-withdrawals-list). # Save a bank destination Source: https://docs.agentcard.sh/companies/api/reference/wallet-withdrawal-recipient-create openapi.json POST /api/v2/wallet/withdrawal-recipients Saves a bank account the user can withdraw to. `ach` needs `routing_number`, `account_number`, and `account_type`; `international_wire` needs `iban` and `swift_code`. Some countries need extra fields via `country_specific` (for example `ifsc`, `clabe`, `bsb`); the validation error names any missing key. A user can hold up to 25 active destinations. ## How it works Before a user can withdraw to a bank, they save the destination once. Collect the bank details in your UI and pass them through; we validate per type and return the destination **masked** (last four digits only) — store the `id` and render the masked fields, never the raw numbers. * **US accounts** (`type: "ach"`): `routing_number` (9-digit ABA), `account_number`, `account_type`. * **Everywhere else** (`type: "international_wire"`): `iban` + `swift_code`. Some countries need one extra field via `country_specific` — for example `{"ifsc": "..."}` for India, `{"clabe": "..."}` for Mexico, `{"bsb": "..."}` for Australia. For those common cases a missing key is rejected as `recipient_fields_invalid` with the field named; for other countries, include the fields the destination bank requires. * A user can hold up to **25 active destinations**; remove one to add more. The full details are only used by our team to execute the transfer — they never appear in any read endpoint. # Remove a bank destination Source: https://docs.agentcard.sh/companies/api/reference/wallet-withdrawal-recipient-delete openapi.json DELETE /api/v2/wallet/withdrawal-recipients/{recipient_id} Soft-removes a saved destination. Withdrawals already requested against it are unaffected. ## How it works Soft-removes a saved destination: it stops appearing in the list and can no longer be used for new withdrawals. Withdrawals already requested against it are unaffected and still complete. # List saved bank destinations Source: https://docs.agentcard.sh/companies/api/reference/wallet-withdrawal-recipients-list openapi.json GET /api/v2/wallet/withdrawal-recipients The user's active bank destinations, masked. Removed destinations never appear. ## How it works Returns the user's active bank destinations, masked. Use it to render a "withdraw to" picker: show `nickname` (or `beneficiary_name`) plus `account_number_last4` / `iban_last4`, and pass the chosen `id` as `recipient_id` when [creating a withdrawal](/companies/api/reference/wallet-withdraw). # List withdrawals Source: https://docs.agentcard.sh/companies/api/reference/wallet-withdrawals-list openapi.json GET /api/v2/wallet/withdrawals The user's most recent withdrawals (up to 20), newest first, across every rail. Poll this to reflect status changes in your UI; `completed` and `rejected` are terminal and also emailed to the user. ## How it works The user's most recent withdrawals (up to 20), newest first, across every rail. Poll it to reflect status changes in your UI — a few times a day is plenty, since transfers are executed by a human within 1-3 business days. | Status | Meaning | | ------------ | ----------------------------------------------------------------- | | `requested` | Received; the balance is held. The user got a confirmation email. | | `processing` | The transfer is being executed. | | `completed` | Sent. Terminal; the user got an email. | | `rejected` | Not executed; the hold is released. `failure_code` says why. | # How it works Source: https://docs.agentcard.sh/get-started/how-it-works How an Agentcard integration fits together, from the wallet to the money behind it. An Agentcard integration has four steps. Your server authenticates with us. You connect a user. The user gets a card in their wallet. Then their agent spends, and we send you webhooks so you know what happened. That's the whole system. The rest of this page covers the concepts, how the money actually moves, and how testing works. When you start wiring it up, [Connect users](/wallet/connect-users) covers the credentials in detail. ## Concepts * The **Agentcard wallet** stores cards: your users' credit and debit cards, and the Agentcards we issue. * **Balance** is the money. Users add balance, and balance is what backs an Agentcard. * **Agentcards** are the cards we issue. The cards users bring themselves are their bank cards. * **Purchase** is how agents buy things. The endpoint is called `buy`. ## How money moves When a user adds their own card, there is nothing to prepay. Their agent buys something and the purchase gets charged to their card, like any other charge. When a user has an Agentcard, the card spends from balance that was added beforehand. It can never spend more than the balance behind it. In both cases you can set limits on each card, lock a card to a single merchant, and require an approval when a purchase needs a human to say yes. ## Test vs live There is one API: `api.agentcard.sh`. Whether you are in sandbox or production depends on which credentials you authenticate with, not on the URL. Sandbox never sends email or SMS, and the verification code is always `111111`. No real money moves. The full list of sandbox tools is in [Test in sandbox](/ship/test-in-sandbox). When you're ready, the [Quickstart](/get-started/quickstart) walks through all of this with real calls. # Quickstart Source: https://docs.agentcard.sh/get-started/quickstart Connect a test user, open the wallet, add a test card, and make a payment. Everything runs in sandbox. This takes about fifteen minutes. Everything happens in sandbox, so no email gets sent, no money moves, and the verification code is always `111111`. Before you start, open the [dashboard](https://app.agentcard.sh) and go to **Settings → Developers → Credentials**. That's where your sandbox `client_id` and `client_secret` live; if you don't have a client yet, **Implement Agentcard** in the same menu creates one. Exchange your credentials for a bearer token. The request is form-encoded, per OAuth. ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/oauth/token \ -d grant_type=client_credentials \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_CLIENT_SECRET ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/oauth/token", { method: "POST", body: new URLSearchParams({ grant_type: "client_credentials", client_id: "YOUR_CLIENT_ID", client_secret: "YOUR_CLIENT_SECRET", }), }); const { access_token } = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/oauth/token", data={ "grant_type": "client_credentials", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", }, ) access_token = res.json()["access_token"] ``` You get back an `access_token`. Use it as `Authorization: Bearer` on every call below. Start a connection. In sandbox nothing is actually sent, so any email works. ```bash cURL 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": "testuser@example.com"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/start", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ email: "testuser@example.com" }), }); const attempt = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/start", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"email": "testuser@example.com"}, ) attempt = res.json() ``` Verify with the sandbox code: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/connect/verify \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"connect_id": "CONNECT_ATTEMPT_ID", "code": "111111"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/verify", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ connect_id: "CONNECT_ATTEMPT_ID", code: "111111" }), }); const connection = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/verify", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"connect_id": "CONNECT_ATTEMPT_ID", "code": "111111"}, ) connection = res.json() ``` The response includes the user's `user_id` and a connection token. Save both. This also fires your first webhook, `connection.created`. Then record the user's authorization. This is the consent step: it registers that the user agreed to let your product and their agent use their wallet, and wallet links can't be created for them until it's on file. In your product the wallet shows the user a consent screen; in this tutorial you record it directly: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/connect/consent \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/consent", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const data = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/consent", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) data = res.json() ``` ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response includes a `url`. This is what you hand to the wallet in the next step. The same link works on any platform. Put this on any page: ```html theme={null} ``` The wallet opens as a sheet. Add a card with the test number `4242 4242 4242 4242`, any future expiry, any CVC. `onSuccess` fires with `card_attached`, and the `connected_card.updated` webhook goes out. Open the wallet again, this time asking for a payment: ```html theme={null} ``` The user approves, and `onSuccess` fires with `payment_completed`. And that's it. You connected a user, they added a card, and a payment went through. Open the [dashboard](https://app.agentcard.sh) and check **Webhooks → Deliveries**. Every event from this tutorial is in there, with its payload: `connection.created`, `connected_card.updated`, and the payment events. ## Where next In production your server should be listening for those events instead of reading them in the dashboard. The [Webhooks](/wallet/webhooks) page shows how. To put the wallet in your actual product, go to [Choose your platform](/platforms/choose-your-platform). And when you want your agent to actually buy things, that's the [Purchase API](/purchase/purchase-api). # What is Agentcard Source: https://docs.agentcard.sh/get-started/what-is-agentcard Agentcard gives AI agents a way to pay for things online, and an API that completes the purchase for them. Your users have AI agents. Those agents can search, compare prices, and decide what to buy. But when they get to checkout, they have no way to pay. Agentcard solves this with two things that work together. These docs are for companies building Agentcard into a product. If you just want a card for your own agent, read the [Personal docs](/personal/introduction) instead. ## 1. The wallet The Agentcard wallet stores your users' credit and debit cards, along with the Agentcards we issue, so their agents can use them securely. It's a component you put in your product. When a user opens it, they can add a card they already have, or get an Agentcard from us. If they use their own card, they enter it once and their agent can pay with it. They don't need to verify their identity or load money first. If they want an Agentcard, they verify who they are, add balance, and we issue the card. You can limit an Agentcard to a single purchase, lock it to one merchant, or cap what it can spend. Either way, we handle the hard parts inside the wallet: consent, card entry, identity verification, and PCI compliance. Card numbers never touch your servers. ## 2. The Purchase API Having a card is only half the problem. Someone still has to go to the store, log in, and place the order. The Purchase API does that part. Your agent sends plain text, like "order two boxes of diapers from Amazon", and we connect to the merchant, pay with the user's card, and send back the order confirmation. ## What we handle vs what you build | Agentcard handles | You build | | ----------------------------- | --------------------------------------- | | Adding cards, end to end | One server call to create a wallet link | | Identity verification (KYC) | Opening the wallet in your product | | Issuing Agentcards, PCI scope | Listening to webhooks | | Merchant login and checkout | Calling `buy` | ## Where to start If you want to understand the pieces first, read [How it works](/get-started/how-it-works). If you'd rather see it running, the [Quickstart](/get-started/quickstart) gets you to a payment in about fifteen minutes. # Account & Session Source: https://docs.agentcard.sh/personal/cli/account Sign in, connect your agent, and manage connected apps ## Sign in ```bash theme={null} agent-cards signup # Sign up or sign in with an emailed code agent-cards login # Same as signup ``` Both email you a one-time code — enter it at the prompt to finish. Driving the CLI for someone else (no terminal)? `agent-cards login --email their@email.com`, then finish with `--code `. Then: ```bash theme={null} agent-cards whoami # Show the currently signed-in email agent-cards logout # Log out and clear stored credentials ``` ## Link or merge accounts ```bash theme={null} agent-cards account link # prompts for the other account's email or phone agent-cards account link you@old-email.com # or pass it directly ``` Sends a one-time code to that email or phone; enter it to finish. If the identifier belongs to another Agentcard account, the two accounts merge into one (the identity-verified account survives, and both identifiers sign in afterwards); otherwise it is simply added to your account. This is the fix when identity verification is rejected because your documents are already verified on another account. See [Linking accounts](/personal/linking-accounts). ## Connect your agent Configure the Agentcard MCP server in Claude Code in one step: ```bash theme={null} agent-cards setup-mcp ``` Restart your agent session afterward so the tools load. Full instructions and other clients are in [MCP setup](/personal/mcp/overview). ## Connected apps See and revoke third-party apps that have connected to your account via OAuth: ```bash theme={null} agent-cards connections # List connected apps (default) agent-cards connections revoke # Revoke an app's access (-y to skip confirmation) ``` ## Update the CLI ```bash theme={null} agent-cards update ``` The CLI also checks for updates automatically before each command. # For Coding Agents Source: https://docs.agentcard.sh/personal/cli/api The api namespace: list, search, describe, and call every account tool from the terminal `agent-cards api` projects the full Agentcard tool catalog (the same tools the [MCP server](/personal/mcp/overview) serves) through the CLI, built for coding agents: structured JSON on stdout, `{error, hint}` envelopes on failure, exit code 1 on error. No MCP client required; being signed in (`agent-cards login`) is enough. Point an agent at it with one command: ```bash theme={null} agent-cards api --agent-help # prints the full agent guide; have your agent load it into context ``` ## Discover tools ```bash theme={null} agent-cards api tools # every advertised tool name agent-cards api tools --table # human view: boxed table with one-line descriptions agent-cards api tools --all # include the expert shopping and advanced tools hidden by default agent-cards api search 'card|balance' # regex search over names and descriptions agent-cards api describe create_card # what a tool does (alias: api info) agent-cards api schema create_card # its input/output schemas agent-cards api schema create_card amount_cents # drill into one field ``` ## Call tools ```bash theme={null} agent-cards api call get_balance '{}' agent-cards api call create_card '{"amount_cents": 2500}' --confirm echo '{"amount_cents": 2500}' | agent-cards api call create_card --stdin --confirm ``` | Option | Description | | ---------------------- | ---------------------------------------------------------------------- | | `--confirm` | Required for tools that move money or irreversibly change the account. | | `--dry-run` | Validate the arguments against the tool schema without executing. | | `--stdin` | Read the JSON arguments object from stdin. | | `--no-fail` (on `api`) | Always exit 0; errors still print their `{error, hint}` envelope. | ## Skills Published Agentcard skills (markdown playbooks that teach an agent the workflows) install straight from the CLI, sha256-verified: ```bash theme={null} agent-cards api skill list # what's published agent-cards api skill install agent-card # install into .agents/skills/ (add --force to overwrite) ``` See [Agent Skill](/personal/mcp/skill) for what the skill covers. ## Teach your coding agent One command wires a coding agent to drive Agentcard through the `api` namespace: it writes a steering block into the agent's instruction file. ```bash theme={null} agent-cards agents add ``` | Option | Description | | --------------- | ---------------------------------------------------------- | | `--agent ` | Target one agent: `claude-code`, `codex`, or `gemini`. | | `--all` | Wire every detected agent without prompting. | | `--path ` | Write the steering block into a specific instruction file. | Installed steering blocks refresh themselves when the CLI updates, so the instructions never go stale. # Buy Source: https://docs.agentcard.sh/personal/cli/buy Shop and check out at linked merchants from the terminal The `agent-cards buy` surface lets you shop at linked merchants and check out with a virtual card. The fastest way is natural language; granular subcommands exist for scripted or expert use. For the bigger picture, see the [Shopping guide](/personal/shopping). ## Chat to shop ```bash theme={null} agent-cards buy # open the buy chat agent-cards buy the caesar salad from Zuni on doordash # seed the chat with a request agent-cards buy chat --resume # resume a conversation ``` Bare `buy` (or any free-text after `buy` that isn't a subcommand) opens the conversational buy agent, which walks you through finding items, building a cart, and checking out. ## Link a merchant ```bash theme={null} agent-cards buy merchants # list merchants and link status agent-cards buy link --email --first-name --last-name --phone

agent-cards buy confirm --pending --code agent-cards buy connect # link by logging in via a hosted browser (doordash) agent-cards buy unlink # disconnect a merchant ``` Supported merchants include `rappi`, `goodeggs`, and `doordash`. ## Browse and build a cart ```bash theme={null} agent-cards buy stores # find stores within a multi-store merchant (DoorDash) agent-cards buy store # scope searches + cart to one store agent-cards buy search # search a merchant for products agent-cards buy cart # view the cart agent-cards buy add [--quantity ] agent-cards buy qty # set quantity (0 removes) agent-cards buy remove agent-cards buy clear # empty the cart ``` ## Budgets ```bash theme={null} agent-cards buy budget [--timezone ] ``` Set a spend budget where `period` is `daily`, `weekly`, `monthly`, or `total`. ## Check out ```bash theme={null} agent-cards buy checkout --yes ``` Checkout is **destructive** (it places and pays for the order) and requires `--yes`. | Option | Description | | ------------------------- | ----------------------------------------------------------------------- | | `--yes` | Confirm placing the order. | | `--tip ` | Dasher tip in dollars; added to the charge and card size. | | `--schedule ` | Schedule delivery for an ISO-8601 time; default is ASAP. | | `--approval ` | A spend approval id, if checkout returned `needs_approval`. | | `--idempotency-key ` | Reuse the same key on a retry so a timed-out call never double-charges. | ## Orders ```bash theme={null} agent-cards buy orders [--limit ] # recent order history (max 50) agent-cards buy reorder # re-create a cart from a past order agent-cards buy track # status + ETA for a placed order agent-cards buy substitution # similar | refund | contact ``` ## Delivery addresses ```bash theme={null} agent-cards buy addresses # list saved delivery addresses agent-cards buy set-address # set the default delivery address ``` # Cards Source: https://docs.agentcard.sh/personal/cli/cards Create, list, inspect, and close virtual cards Manage virtual cards with the `agent-cards cards` command group. Cards are funded from your [balance](/personal/cli/wallet) — add cash with `agent-cards fund` before creating your first card. ## Create a card Amounts are in **US dollars, not cents**: ```bash theme={null} agent-cards cards create --amount 25 ``` | Option | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------- | | `--amount ` | **Required.** Amount in dollars (e.g. `25` = \$25.00). | | `--multi-use` | Create a multi-use card: stays open across charges until its limit is spent (subscriptions). | | `--preset ` | Merchant-scope preset: `ai_labs` creates an AI card (AI-lab merchants only, boosted tokenback; implies `--multi-use`). | | `--expires ` | Auto-close the multi-use card at this ISO-8601 time (at most 365 days out), e.g. `2027-01-01T00:00:00Z`. | | `--from ` | Issue against a specific [added card](#add-your-own-card) (see `add --list`). | | `-y, --yes` | Skip the confirmation prompt (for non-interactive / agent use). | | `--json` | Output the result as JSON and run fully non-interactively. | The amount must be within your plan's per-card cap ($50 Free / $500 Basic / \$1,000 Pro — see [Plans](/personal/plans)). The card draws on your cash balance when it's actually used. With an added card, it charges that card directly instead. ## Add your own card Add your own Visa card so new cards are created against it and purchases charge it directly, with no identity verification and no balance funding: ```bash theme={null} agent-cards add # start the flow: a secure link (bank one-time code + passkey) agent-cards add --list # show added cards; the top active one is the default for new cards ``` | Option | Description | | --------------- | ------------------------------------------------------------------------ | | `--add` | Add another card alongside the current one (multi-card). | | `--replace` | Remove the currently added card first, then add a new one. | | `--list` | Show all added cards (the top active card is the default for new cards). | | `--remove ` | Remove one added card by id; virtual cards created against it close. | | `--json` | Machine-readable output (a single request, no browser or polling). | `agent-cards cards add` is the same command, and `attach` still works everywhere as an alias. With more than one card added, issue against a specific one with `cards create --from `. If your card isn't eligible (business cards, non-US cards, and some issuers are excluded), `cards create` keeps working the balance-funded way. ## List cards ```bash theme={null} agent-cards cards list ``` Shows each card's ID, last four digits, expiry, balance, and status. ## Card details (PAN / CVV) ```bash theme={null} agent-cards cards details ``` Returns the decrypted full card number, CVV, and expiry — use this to fill a payment form. Only run it when you actually need the credentials. ## Balance ```bash theme={null} agent-cards balance ``` A quick balance check without exposing the card credentials. ## Transactions ```bash theme={null} agent-cards cards transactions ``` | Option | Description | | ------------------- | -------------------------------------------------------------------------------------------- | | `--limit ` | Number of transactions (default 20). | | `--status ` | Filter by status (e.g. `PENDING`, `SETTLED`, `DECLINED`, `REVERSED`, `EXPIRED`, `REFUNDED`). | `agent-cards transactions ` is a top-level alias for the same per-card command. ### All transactions (account-wide) ```bash theme={null} agent-cards transactions ``` Run `transactions` with **no** card id to list every transaction across all of your cards in one flat list. Each row shows which card it belongs to (last 4). | Option | Description | | ------------------- | -------------------------------------------------------------------------------------------- | | `--limit ` | Number of transactions (default 20, max 100). | | `--offset ` | Number of transactions to skip, for pagination. | | `--status ` | Filter by status (e.g. `PENDING`, `SETTLED`, `DECLINED`, `REVERSED`, `EXPIRED`, `REFUNDED`). | ## Close a card ```bash theme={null} agent-cards cards close ``` Closing is permanent and releases any held funds. Pass `-y, --yes` to skip the confirmation prompt. # CLI Overview Source: https://docs.agentcard.sh/personal/cli/overview Manage your cards, balance, and shopping from the terminal The `agent-cards` CLI is the terminal-first way to manage your personal Agentcard account — sign in, add cash to your balance, issue cards, and shop at linked merchants. Personal commands are top level in the `agent-cards` CLI. Companies integrating Agentcard use the same CLI's `companies` namespace (`agent-cards companies …`), see the [Companies docs](/companies/introduction). ## Installation ```bash theme={null} npm install -g agent-cards ``` Update to the latest version any time: ```bash theme={null} agent-cards update ``` If you've wired the CLI into a coding agent with [`agents add`](/personal/cli/api#teach-your-coding-agent), the installed steering blocks refresh themselves when the CLI updates. ## Authentication Most commands require you to be signed in. Run `agent-cards signup` (or `login` — they're the same): a sign-in link and short code appear, and you approve in your browser (any device). On machines where the browser flow isn't available, we email you a one-time code instead. Credentials are stored locally; clear them with `agent-cards logout`. ## Command groups | Group | Description | | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | | `signup` / `login` / `logout` / `whoami` | [Account & session](/personal/cli/account) | | `wallet` / `balance` / `fund` / `withdraw` | [Wallet & balance](/personal/cli/wallet): see your cards and cash, add cash, withdraw | | `redeem` / `codes` | [Redeem a promo code](/personal/cli/wallet#redeem-a-promo-code) into your balance; list your codes | | `cards` | [Create, list, inspect, and close cards](/personal/cli/cards) | | `add` | [Add your own card](/personal/cli/cards#add-your-own-card) so purchases charge it directly (also `cards add`) | | `transactions` | Per-card or account-wide [transaction history](/personal/cli/cards) | | `kyc` | Run one-time identity verification from the terminal ([KYC & approvals](/personal/kyc-and-approvals)) | | `account` | Link another email or phone, or merge two accounts ([Linking accounts](/personal/linking-accounts)) | | `connections` | List or revoke third-party apps connected to your account | | `approvals` | List, approve, or deny [requests from connected apps](/personal/kyc-and-approvals#approval-requests) waiting on you | | `rewards` | Tokenback: view the token balance and redeem it ([Rewards](/personal/concepts/rewards)) | | `payment-method` | [Manage saved payment methods](/personal/cli/payment-methods) | | `plan` | [View and change your subscription](/personal/cli/plan) | | `settings` | [Notification and authorization preferences](/personal/cli/settings) | | `buy` | [Shop and check out at linked merchants](/personal/cli/buy) | | `api` | [The tool catalog for coding agents](/personal/cli/api): list, search, describe, and call every account tool | | `agents` | [`agents add`](/personal/cli/api#teach-your-coding-agent) teaches Claude Code, Codex, or Gemini to drive Agentcard | | `setup-mcp` | Configure the Agentcard MCP server in Claude Code | | `update` | Update the CLI in place | | `support` | Start a live support conversation | ## Global options ``` -V, --version Show version number -h, --help Show help for any command --help --full Every command, every group, one board --api-url API base URL for this invocation (same as AGENT_CARDS_API_URL) ``` Use `--help` on any subcommand for details: ```bash theme={null} agent-cards cards create --help ``` ## Agent-friendly flags Several commands support non-interactive use so an agent can drive them without prompts: * `cards create --amount --yes` skips the confirmation prompt; add `--json` for machine-readable output. * `cards close --yes` skips the close confirmation. * `add --json` runs the add-card flow non-interactively (a single request, no browser or polling). * `buy checkout --yes` confirms placing an order. * The whole [`api` namespace](/personal/cli/api) is built for agents: JSON on stdout, `{error, hint}` envelopes on failure. # Payment Methods Source: https://docs.agentcard.sh/personal/cli/payment-methods Save, list, default, and remove the cards that fund your virtual cards Saved payment methods are charged **only for flight bookings** — virtual cards are funded from your balance (`agent-cards fund`). Manage them with the `agent-cards payment-method` command group (singular `payment-method`). ## Save a payment method ```bash theme={null} agent-cards payment-method setup ``` Opens a secure Stripe checkout URL — save your card there. Once saved, it's used automatically when you create cards. ## List saved methods ```bash theme={null} agent-cards payment-method list ``` Shows each method's ID, brand, last four, and expiry, and marks which one is the default. ## Set the default ```bash theme={null} agent-cards payment-method default # prompts you to choose agent-cards payment-method default # set a specific method ``` The default payment method is the one charged when you book a flight. ## Remove a method ```bash theme={null} agent-cards payment-method remove # prompts you to choose agent-cards payment-method remove --id # remove a specific method ``` # Plan Source: https://docs.agentcard.sh/personal/cli/plan View and change your subscription Manage your subscription with the `agent-cards plan` command group. See [Plans](/personal/plans) for a full comparison of limits. ## View your plan ```bash theme={null} agent-cards plan ``` Shows your current plan, per-card amount cap, cards used and remaining this month, and renewal or cancellation status. ## Upgrade ```bash theme={null} agent-cards plan upgrade # defaults to basic agent-cards plan upgrade basic # $15/mo agent-cards plan upgrade pro # $100/mo ``` Returns a Stripe Checkout URL — complete payment in your browser and the plan updates automatically. Confirm with `agent-cards plan`. | Plan | Price | Cards / month | Max per card | | ----- | -------- | ------------- | ------------ | | Free | \$0 | 5 | \$50 | | Basic | \$15/mo | 15 | \$500 | | Pro | \$100/mo | 50 | \$1,000 | ## Cancel ```bash theme={null} agent-cards plan cancel ``` Cancels your paid subscription and reverts to Free at the end of the billing period. # Settings Source: https://docs.agentcard.sh/personal/cli/settings Notification, delivery address, and authorization preferences Manage account preferences with the `agent-cards settings` command group. ## View settings ```bash theme={null} agent-cards settings ``` Shows your current notification preferences, default payment source, default delivery address, and authorization settings. ## Default card ```bash theme={null} agent-cards settings default-card ``` Pick what your agents charge by default: your wallet balance (single-use cards mint from it) or one of your attached cards. Reset to auto — an active attached card wins, otherwise the balance — with: ```bash theme={null} agent-cards settings default-card --clear ``` ## Delivery address ```bash theme={null} agent-cards settings address ``` Set the default delivery address your agents ship to when you shop through them ([shopping](/personal/shopping)). Agents confirm this address instead of asking you to dictate one on each order. Include a phone number — retail shipping carriers require one. Remove it with: ```bash theme={null} agent-cards settings address --clear ``` ## Notifications ```bash theme={null} agent-cards settings notifications ``` Configure the email notifications you receive (for example, card creation and approval requests). ## Authorization ```bash theme={null} agent-cards settings authorization ``` Shows your authorization status. Authorization is always enabled — sensitive actions such as revealing card details may require email approval. See [KYC & approvals](/personal/kyc-and-approvals). # Wallet & balance Source: https://docs.agentcard.sh/personal/cli/wallet Your wallet holds your cards; your balance is the cash that funds them Your **wallet** is the container of your cards, like Apple Wallet. Your **balance** is the cash that funds new cards — held in USDC, but you add and withdraw in USD. ## Show your wallet ```bash theme={null} agent-cards wallet ``` Shows the cards in your wallet, then your balance (with any deposit still settling). The first run provisions everything automatically. ## Check your balance ```bash theme={null} agent-cards balance # your cash balance (add --json for scripts) agent-cards balance # one card's remaining balance ``` ## Add cash ```bash theme={null} agent-cards fund --amount 50 ``` | Option | Description | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `--amount ` | **Required.** Amount in dollars (e.g. `50` = $50.00), up to $10,000.00. The minimum is $2.00 or $20.00 depending on your funding provider — the error tells you your exact range. | | `--method ` | `apple_pay` (default) or `google_pay`. | Opens a checkout page in your browser — pay with Apple Pay or Google Pay and the cash lands in your balance within minutes. First-time funding asks for a one-time verification code (sent by text or email); it stays fresh for 60 days. Brand-new account? Identity verification comes first. Run `agent-cards cards create` — it walks you through identity (ID photo + a short face scan), sends the one-time code, and funds your balance in the same flow. After that one-time step, `agent-cards fund` works directly. ## Redeem a promo code ```bash theme={null} agent-cards redeem SUMMER25 # the credit lands in your balance agent-cards codes # your code history: used, processing, retryable ``` Each code works once per user. The credit becomes spendable within a minute or two; `agent-cards balance` shows it land. If a redemption fails mid-transfer, nothing is consumed: `agent-cards codes` marks it retryable and you can run `redeem` again with the same code. Both commands take `--json` for scripts. ## Withdraw ```bash theme={null} # To your bank account (picks a saved account, or walks you through adding one) agent-cards withdraw --amount 25 # As USDC on Base, to an address you control agent-cards withdraw --amount 25 --to 0xYourWallet... ``` | Option | Description | | -------------------- | ----------------------------------------------------------------- | | `--amount ` | **Required.** Amount in dollars, $2.00 to $10,000.00 per request. | | `--to

` | Send USDC on Base to this `0x` address instead of a bank account. | Withdrawals are **processed by our team**, usually within **1-3 business days** — you get an email when the request is received and another when it's sent. Open requests hold your balance until they complete, so you can never over-request. **Bank accounts** are saved once and reused: US accounts use ACH (routing + account number), everywhere else uses an international wire (IBAN + SWIFT). Some countries need one extra identifier — the CLI prompts for it (IFSC in India, CLABE in Mexico, BSB in Australia). Account numbers are masked everywhere after saving. **Crypto withdrawals** go to any address on Base — double-check it, crypto transfers can't be reversed. Agentcard-managed addresses are rejected. If you connected through a company's app and withdrawals are unavailable, that company has disabled them — contact them. The older spellings `agent-cards wallet fund` and `agent-cards wallet withdraw` keep working as aliases. # Cards Source: https://docs.agentcard.sh/personal/concepts/cards How personal virtual cards work A card is a virtual Visa your agent can use to make purchases. Cards are single-use by default: they close on their own after the first approved charge. Each card you create is funded from your cash balance and only charged when the card is actually used. ## Lifecycle 1. **Create** — You issue a card with a spend limit (within your plan's per-card cap). The amount is reserved from your balance's spending power. 2. **Use** — Your agent makes purchases with the card number, expiry, and CVV — entered at checkout, or end to end through the [`buy`](/personal/shopping) surface. Funds are captured only as transactions settle. 3. **Close** — Closing a card is permanent and returns any unspent funds to your balance. ## Card types Cards come in two types, chosen at creation with the `type` parameter: | | Single-use (default) | Multi-use | | ----------- | ---------------------------------------------------- | --------------------------------------------------------------- | | Charges | One: the card closes after its first approved charge | Any number, until the total limit is spent | | Best for | One-off purchases | Subscriptions and recurring merchants | | Spend limit | Fixed at creation | Adjustable while the card is open | | Closes | Automatically after the purchase | When the limit is exhausted, its expiry passes, or you close it | Create a multi-use card by passing `type: "multi_use"`. You can give it an expiry with `expires_at` (an ISO-8601 timestamp with timezone, in the future, at most 365 days out); when the expiry passes, the card closes and any unspent funds return to your balance. While a multi-use card is open, you can manage it: * **Pause and resume**. Pausing blocks new charges immediately and is fully reversible; the balance and limit are untouched, and the reserved funds stay reserved. A paused card still counts toward your active multi-use card cap. * **Change the total limit**. Raising the limit reserves the difference from your balance's spending power, and fails with `insufficient_collateral` if the balance cannot cover it; lowering it releases the difference back. The limit can never go below what the card has already spent. ### Active multi-use cards per plan Your plan caps how many multi-use cards you can have active (open or paused) at once: | Plan | Active multi-use cards | | ----- | ---------------------- | | Free | 0 | | Basic | 2 | | Pro | 10 | Closing a card frees its slot immediately. Cards issued through a company connection are governed by the organization, not your personal plan; they have no active-card cap. ### AI cards An AI card is a multi-use card locked to AI-lab merchants (OpenAI, Anthropic, Gemini). Create one by passing `scope_preset: "ai_labs"`; the preset implies `type: "multi_use"`, so you don't need to pass both. The restriction is enforced when a charge is authorized, based on the merchant's category: charges outside the allowed categories decline, and refunds are always allowed. AI cards earn boosted [tokenback](/personal/concepts/rewards) on eligible AI-lab spend, and you can have up to 10 active AI cards at a time. AI cards are available everywhere cards are created: MCP (`create_card` with `scope_preset`), the CLI (`agent-cards cards create --preset ai_labs`), and the API (`scope_preset` on card creation). ## Where cards work Cards are for **online checkout**: your agent enters the card number, expiry, and CVV wherever Visa is accepted on the web, or buys end to end through the [`buy`](/personal/shopping) surface. Cards can't be added to Apple Wallet or Google Wallet — Agentcard doesn't support push provisioning, so there's no tap-to-pay or other in-store use. Apple Pay and Google Pay appear in Agentcard only as ways to top up your balance via `add_funds` (see [Payment Methods](/personal/payment-methods)), not as ways to use a card. ## Properties * **Spend limit** — The cap set when you create the card (adjustable later on a multi-use card); your agent can never spend more than this. * **Balance** — The remaining amount as transactions settle. Check it with `get_card_balance` / `agent-cards balance ` without exposing the credentials. * **Status** — `OPEN` while usable, `PAUSED` while a multi-use card is paused, `CLOSED` once closed. * **Credentials (PAN / CVV / expiry)** — Revealed only on explicit request via `get_card_details` / `agent-cards cards details `, which may require approval. ## Limits The maximum amount per card depends on your plan: | Plan | Max per card | Cards / month | | ----- | ------------ | ------------- | | Free | \$50 | 5 | | Basic | \$500 | 15 | | Pro | \$1,000 | 50 | See [Plans](/personal/plans) to upgrade. [Test cards](/personal/concepts/test-mode) (from an organization's sandbox integration) don't count toward your monthly quota. ## One wallet across apps You have one wallet, whatever app is looking at it. Cards you create from the dashboard, the CLI, or any connected app all live in it, and every connected app can see them, each card tagged with the app or company that minted it. Seeing is not acting: an app can only manage the cards it minted itself. When a different app tries to act on one of your cards (view its full details, pause, resume, close, or change its limit), the action pauses and you get an **approve/deny email**. Nothing happens until you approve, and each approval covers exactly that one action. Cards issued by a company you're linked to stay read-only outside that company's own app. ## Safety * Card numbers and CVVs are never displayed unless you explicitly ask. * Closing a card is always confirmed first. * Every card has a spend limit, so a card can never draw more from your balance than its current limit. Raising a multi-use card's limit reserves the difference from your balance up front. * An app you connected can't act on cards another app minted without your emailed approval (above). # Tokenback Source: https://docs.agentcard.sh/personal/concepts/rewards A share of every purchase comes back to you as AI spending power Tokenback is Agentcard's reward program: a share of every purchase comes back to you as **AI spending power**. Your tokenback balance grows as your agents spend, and you redeem it onto your rewards card, which works at AI labs (OpenAI, Anthropic, Gemini). ## How tokenback is earned Tokenback accrues when a charge settles, not when it is first authorized. There are three sources: * **Every purchase**. Personal cards funded from your own balance earn tokenback on all spend, at any merchant — currently **1% of the settled amount**. The rate is stamped on each card when it is created, so rate changes never affect cards you already hold. * **AI card boost**. [AI cards](/personal/concepts/cards#ai-cards) earn tokenback on eligible AI-lab purchases (OpenAI, Anthropic, Gemini). * **Company-routed share**. Companies that issue cards through Agentcard earn a share of settled card volume, and they can route part of that share to their users as tokenback. If you connected through a company that has this enabled, spending on its cards earns you tokenback automatically. When a charge that earned tokenback is refunded, the earned amount is clawed back from your balance. **For companies**: route part of your interchange earnings to your users by setting the reward share on your dashboard's Earnings page. Each grant fires a [`reward.earned`](/wallet/webhooks#reward-earned) webhook to your company. ## Redeeming tokenback Tokenback is redeemed onto your **rewards card**: a dedicated card Agentcard mints for you on your first redemption and tops up on every redemption after. It is what makes tokenback AI spending power rather than cashback: * **AI labs only.** The rewards card is merchant-locked to AI labs (OpenAI, Anthropic, Gemini) — charges anywhere else decline at authorization. Your general spend earns tokenback; the reward comes back as AI spending power. * **Permanent.** It keeps the same card number for life: spending it to zero never closes it (it just declines until your next redemption tops it up), it never expires, and it never counts against your plan's card limits. Put it on file at your AI labs once. * **Close-protected.** Agents and casual closes can't retire it; closing it requires an explicit confirmation, returns its funds to your balance, and your next redemption mints a fresh card. The minimum redemption is **\$5.00** of tokenback. The value always lands in your balance first; if the card delivery is momentarily behind, it is spendable as cash right away and moves onto the rewards card within a few minutes. Check your balance and redeem from any of these surfaces: * **Dashboard**: the Tokenback page shows your balance and activity, and lets you redeem. * **MCP**: `get_rewards` returns the balance and recent activity; `redeem_rewards` redeems it. * **CLI**: `agent-cards rewards` shows the balance; `agent-cards rewards redeem` redeems it. Redeeming tokenback directly for AI-lab API credits is planned. # Test Cards Source: https://docs.agentcard.sh/personal/concepts/test-mode Why a card can be marked TEST, and how to get live cards Personal accounts are **always live**. Every card you create is real: it draws on your cash balance when used, within your plan's limits. There is no test mode to switch on or off on your account. A TEST card comes from a connection that isn't fully live. In practice that means an **organization's sandbox integration**: a company you connected to (via their app or platform) that is still running on sandbox credentials, typically while they build their integration. (A stale connection whose app was deleted also falls back to TEST cards; reconnecting fixes that.) Cards created either way are clearly flagged: | | Test card | Live card | | -------------- | ---------------------- | -------------------------- | | Number | Starts `4242…` | Real card number | | Charges | Never charged for real | Draws on your cash balance | | Real merchants | Not usable | Usable | | Monthly quota | Doesn't count | Counts toward your plan | ## If you expected a live card and got a TEST one 1. **Connected through a company's app or platform?** Their integration is running in sandbox. Ask that company to move you to their production integration, or connect your agent directly to your own account instead. 2. **Old or broken connection?** A connection whose app was deleted falls back to TEST cards. Remove the connection and connect your agent again to get a fresh live one. 3. **On a live connection**, real cards need two one-time steps: identity verification (your agent walks you through it, or `create_card` returns a verification link) and money in your balance (`add_funds`, Apple Pay or Google Pay). See the [Quickstart](/personal/quickstart) for the full flow from signup to first live card. # Introduction Source: https://docs.agentcard.sh/personal/introduction Give your own AI agent a virtual card Agentcard Personal lets an individual create and manage virtual Visa cards that their AI agent can use to make purchases on the web. You sign in with your email, add cash to your balance, and issue cards with a fixed spend limit — backed by your cash balance and only charged when the card is actually used. This is the **Personal** product, built for one person managing their own cards. If you're a company issuing cards to your end-users programmatically, see the [Companies](/get-started/what-is-agentcard) docs instead. ## Two ways to use it Connect `mcp.agentcard.sh/mcp` to Claude Code, Cursor, Claude Desktop, or any MCP client. Your agent gets tools to create cards, check balances, and pay for things — authenticated with your own OAuth login. Manage everything from your terminal with the `agent-cards` CLI — sign up, add cash, create cards, and shop. ## How it works 1. **Sign in** — `agent-cards signup` (or connect the MCP server) and approve the sign-in in your browser; we fall back to an emailed one-time code where a browser isn't available. 2. **Add cash to your balance** — Add money with Apple Pay or Google Pay via a secure checkout link. Your cash balance funds the virtual cards you create. 3. **Create a card** — Issue a virtual Visa with a spend limit. It draws on your cash balance when the card is used. 4. **Give it to your agent** — Your agent uses the card number, expiry, and CVV to check out. 5. **Monitor and close** — Track balances in real time and close cards when done to return unspent funds to your balance. ## Key features * **Fixed-limit cards** — Each card is backed by your cash balance with a fixed cap, so your agent can never overspend. * **Per-card limits** — Every card has a fixed spend limit (up to $50 on Free, $500 on Basic, \$1,000 on Pro — see [Plans](/personal/plans)). * **Live by default** — Cards are real and draw on your cash balance. A card marked TEST comes from a connection that isn't live, usually a company's test-mode integration ([Test cards](/personal/concepts/test-mode)). * **Shop in natural language** — The [`buy`](/personal/shopping) surface lets your agent order from linked merchants end-to-end. * **Safe by design** — Card numbers are never shown unless you ask; closing a card is always confirmed. ## Next steps Sign up, add cash to your balance, and create your first card. Compare Free, Basic, and Pro limits. # KYC & Approvals Source: https://docs.agentcard.sh/personal/kyc-and-approvals One-time identity verification and approval requests Two safeguards gate sensitive actions on a personal account: a one-time identity check before your first card, and approval requests for high-trust actions. ## One-time KYC Before you can create your first card, the card issuer requires identity verification. It runs **in the conversation**: you share a photo of your government ID (driver's license, state ID, or passport), the details are read off it automatically, you confirm them, and the only browser step is a short face scan at the end. You're never asked about occupation, income, or spending plans. The flow, in order: 1. **ID photo** — your agent asks for a photo of your ID (a file path, or a secure upload link that works with a phone camera). The verification provider reads the printed fields off the document automatically; you review and confirm every value before anything is submitted. 2. **Whatever the ID didn't carry** — usually just your SSN (IDs don't print it) and a phone number. The SSN goes straight to the verification provider and is never stored by Agentcard. 3. **Terms** — one explicit yes to the card issuer's cardholder terms. 4. **Face scan** — a link opens a short selfie check in your browser. Verification usually completes in about a minute, and your agent picks up right where you left off. Run `agent-cards kyc` (or just `agent-cards cards create` — it walks you through verification the first time). `create_card` reports `kyc_required`; the agent drives `start_kyc` → `submit_kyc_document` → `submit_kyc_fields` → `get_kyc_status`, asking you only for the photo, the missing fields, and the face scan. You only do this once. Rejected because your documents are **already verified on another Agentcard account**? Re-submitting them can never fix that. Link the other account instead and your verification carries over: see [Linking accounts](/personal/linking-accounts). ## Approval requests Some high-trust actions — such as revealing full card details (`get_card_details`) or certain spend — can trigger an **approval request** (HTTP 202). When that happens: 1. An email is sent to the account owner. 2. The owner approves from the email. 3. Your agent calls `approve_request` with the approval ID (or you approve from the link), then the original action proceeds. You can also review and resolve everything waiting on you from the CLI: ```bash theme={null} agent-cards approvals # the inbox: app, action, card, expiry agent-cards approvals approve # restates the ask and confirms (-y skips) agent-cards approvals deny ``` All three take `--json` for scripts (`approve --json` requires `-y`, since approving records your consent). Authorization is always enabled on your account; you can review its status with `agent-cards settings authorization`. ## Other gating responses | Response | Meaning | What to do | | ----------------------------- | ---------------------------------------------- | ------------------------------------------- | | `user_info_required` | First-time KYC not yet submitted | Submit identity info, then retry. | | `approval_required` (202) | Action needs owner approval | Approve from email, then `approve_request`. | | `payment_method_required` | No saved payment method (flight bookings only) | Run `setup_payment_method` first. | | `beta_capacity_reached` (403) | You've been waitlisted | Wait — nothing else to do. | ## Security defaults * Card numbers and CVVs are **never shown** unless you explicitly request them. * Closing a card is always confirmed (it's irreversible). * Every card has a fixed spend limit, so it can never draw more from your wallet than the amount it was created with. # Linking Accounts Source: https://docs.agentcard.sh/personal/linking-accounts Merge two Agentcard accounts, or add a second email or phone to one If the same person ends up with two Agentcard accounts (one under an old email, one under a phone number), linking joins them into a single account. You prove you control the other identifier with a one-time code, then one of two things happens: * **The identifier belongs to another Agentcard account**: the two accounts **merge**. Cards, balance, identity verification, and history all end up on one account, and both identifiers sign in to it afterwards. * **Nothing uses the identifier yet**: it is simply added to your current account as a second email or phone. ## When you need this * **Identity verification was rejected as a duplicate.** Your documents are already verified on another Agentcard account, so re-submitting them can never succeed. Linking that account is the fix: after the merge your verification carries over, with no new documents needed. * **You want one account.** You signed up twice (a different email, or email vs. phone) and want your cards, balance, and history in one place. ## How to link When verification is rejected as a duplicate, the verification page at [app.agentcard.sh/dashboard/verify](https://app.agentcard.sh/dashboard/verify) shows a **Link that account** flow inline: enter the other account's email or phone, then the one-time code it receives. ```bash theme={null} agent-cards account link # prompts for the other email or phone agent-cards account link you@old-email.com # or pass it directly ``` The `agent-cards kyc` flow prints this command when it hits a duplicate rejection. The `link_account` tool runs the same two steps: call it with `{ type: "email" | "phone", identifier }` to send the code, then again with `{ ticket, code }` to verify it. ## Merge semantics * The **identity-verified account survives**; the other folds into it. * Cards, cash balance, verification, and history all live on the surviving account. Nothing is lost. * **Both identifiers sign in** to the merged account, so your current login keeps working. ## If both accounts are verified A merge between two accounts that have **both** completed identity verification is refused (`both_kyc_approved`). That one is a support case: email [support@agentcard.sh](mailto:support@agentcard.sh). # MCP Overview Source: https://docs.agentcard.sh/personal/mcp/overview Connect Agentcard to your AI agent via MCP The Agentcard MCP server gives your AI agent tools to manage virtual cards, pay for things, and shop — authenticated with your own OAuth login. It's the recommended way to use Agentcard from Claude Code, Cursor, Claude Desktop, and other MCP clients. * **Endpoint:** `https://mcp.agentcard.sh/mcp` * **Transport:** Streamable HTTP * **Auth:** OAuth 2.0 — sign in with your Agentcard account; no API keys ## Install the Agentcard MCP One command: ```bash theme={null} npx -y agent-cards setup-mcp ``` That's it. Restart your Claude Code session so the tools load, then run any Agentcard tool — a browser sign-in appears the first time, and new emails get an account on the spot. To add it manually instead: ```bash theme={null} claude mcp add --transport http agent-cards https://mcp.agentcard.sh/mcp ``` ## Other clients Add to the agent's MCP config (`.cursor/mcp.json`, `.windsurf/mcp.json`, etc.): ```json theme={null} { "mcpServers": { "agent-cards": { "url": "https://mcp.agentcard.sh/mcp" } } } ``` 1. Open **Settings → Integrations**. 2. Click **Add Integration**. 3. Enter `https://mcp.agentcard.sh/mcp`. 4. OAuth handles authentication automatically. Add to `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "agent-cards": { "url": "https://mcp.agentcard.sh/mcp" } } } ``` After connecting, restart your agent session before expecting the tools to appear. The first tool call triggers the OAuth sign-in flow in your browser. ## First steps in your agent Once connected, your agent can call `get_instructions` for the current usage guide, then `list_cards` to orient. Creating the first card walks through funding the wallet and one-time KYC — see [KYC & approvals](/personal/kyc-and-approvals). The full tool catalog is in the [Tools reference](/personal/mcp/tools). # Agent Skill Source: https://docs.agentcard.sh/personal/mcp/skill Teach your agent Agentcard workflows with the installable skill The Agentcard **skill** is a markdown playbook your agent loads alongside the MCP server. The MCP server provides the tools; the skill teaches the agent how to use them well — wallet funding and the one-time verification flow, the card-creation ladder (identity → verification → funding), single-use vs multi-use cards, shopping with `buy`, safety rules (never expose PAN/CVV unprompted, confirm before spending), and error recovery. ## Install With the CLI (sha256-verified): ```bash theme={null} agent-cards api skill install agent-card ``` `agent-cards api skill list` shows everything published. Or via skills.sh: ```bash theme={null} npx skills add tiny-agent-company/agent-card-skill ``` Works with Claude Code, Cursor, Cline, Windsurf, and any agent that supports [skills.sh](https://skills.sh). ## Get it directly The skill is also published at a stable URL your agent can fetch: ``` https://www.agentcard.sh/.well-known/agent-skills/agent-card/SKILL.md ``` The machine-readable index (with checksum) lives at `https://www.agentcard.sh/.well-known/agent-skills/index.json`. ## Pair it with the MCP server The skill assumes the Agentcard MCP server is connected — see the [MCP Overview](/personal/mcp/overview). One-liner for Claude Code: ```bash theme={null} agent-cards setup-mcp ``` You need both: the skill is the *how* (workflows, guardrails), the MCP server is the *what* (the tools themselves). # Tools Reference Source: https://docs.agentcard.sh/personal/mcp/tools Every tool the Agentcard MCP server exposes All tools are prefixed `mcp__agent-cards__*` in your client. Amounts are in **cents** (e.g. \$25 = `2500`); always display them back as dollars. Tools marked **advanced** are not advertised in the default `tools/list` but remain callable by name; browse the full surface with `agent-cards api tools --all`. The granular `buy_*` shopping tools are hidden the same way behind the conversational `buy` tool. ## Getting started | Tool | Purpose | | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `get_instructions` | Return the current usage guide — call this first. | | `whoami` | Show which account this session operates: email, name, plan, KYC + account status, and whether it's a personal login or a third-party OAuth connection. | | `submit_user_info` | Submit the one-time basics (phone number, `terms_accepted`) required before the first card. | | `link_account` | Add another email or phone, or merge a second Agentcard account you own (two-step one-time code; the identity-verified account survives). The fix for a duplicate-identity KYC rejection. See [Linking accounts](/personal/linking-accounts). | | `list_pending_approvals` | List the approval requests waiting on your decision (the asks from connected apps). The agent surfaces them; you decide. | | `approve_request` | Approve or deny a pending approval request. | ## Identity verification (KYC) Conversational identity verification — see [KYC & approvals](/personal/kyc-and-approvals). | Tool | Purpose | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `start_kyc` | Start (or resume) identity verification; returns the next step or a secure verification link. | | `get_kyc_status` | Poll the verification status until it lands. | | `submit_kyc_document` | Submit a photo of the government ID (front, and back when asked). | | `check_kyc_document` | Check what the submitted document was read as. | | `submit_kyc_fields` | Fill any fields the document didn't cover. | | `complete_kyc_transfer` | *Advanced.* One-time migration for accounts verified under the legacy flow (before July 2026) so funding can reuse the verification. | ## Cards | Tool | Purpose | | ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `list_cards` | List all cards with IDs, last four, expiry, balance, and status, including (under the shared wallet) cards other connected apps minted. | | `create_card` | Create a new virtual Visa card (`amount_cents`; funded from your balance, or minted against your attached card when you have one; `source: "issued"` forces the balance). | | `add_card` | Add your own Visa card so purchases charge it directly, with no KYC and no balance funding. Two-phase: send the user the secure link, then call again to confirm. Renamed from `attach_card` in August 2026. | | `list_added_cards` | List the added-card enrollments; the row marked `isDefault` is what new cards charge — your chosen default card (`update_settings` `default_payment`), else the newest active; no row is marked when your default payment is the wallet balance (`create_card` picks a one-off via `connected_card_id`). Renamed from `list_attached_cards` in August 2026. | | `remove_added_card` | Unenroll an added card (virtual cards created against it close first). Always confirm with the user. Renamed from `remove_attached_card` in August 2026. | | `get_card_balance` | Check a card's live balance without exposing credentials. Renamed from `check_balance` in July 2026. | | `get_card_details` | Get decrypted PAN, CVV, and expiry (may require approval). | | `pause_card` / `resume_card` | Reversibly block and unblock charges on a multi-use card. | | `update_card_limit` | Raise or lower a multi-use card's total limit. | | `close_card` | Permanently close a card (irreversible). | | `list_transactions` | Transactions with amount, merchant, status, and timestamps: one card with `card_id`, or the whole account without it. | | `list_all_transactions` | *Advanced.* The account-wide flat list (now folded into `list_transactions`). | | `list_transactions_by_payment_method` | *Advanced.* Transactions grouped by payment method — balance (USDC) spend, saved-card spend, test spend, and Apple Pay / Google Pay deposits — with merchant info and the buy order behind each purchase. | ## Balance | Tool | Purpose | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `get_balance` | Show the cash balance (sets the account up on first use). Renamed from `get_wallet` in July 2026. | | `add_funds` | Add USD via Apple Pay / Google Pay — returns a checkout link for the user. Sends the phone verification code automatically when one is needed. Renamed from `fund_wallet` in July 2026. | | `start_phone_verification` | Re-send the funding one-time code (fresh for 60 days once verified). | | `verify_phone` | Check the one-time code the user reads back. | | `redeem_code` | Apply a promo code and credit the balance (once per user per code). | | `list_codes` | *Advanced.* Show which promo codes the user has used and which are still available to retry. | | `submit_funding_profile` | *Advanced.* One-time funding questionnaire (five multiple-choice answers) so funding checkouts skip the provider's own. | | `create_withdrawal_recipient` | Save a bank account as a withdrawal destination (US ACH, or international IBAN + SWIFT; some countries need one extra field like IFSC/CLABE/BSB). | | `list_withdrawal_recipients` | List the saved bank destinations, masked (bank name and last four only); use an id as `recipient_id` with `withdraw`. | | `withdraw` | Withdraw to the saved bank account, or as USDC on Base with `destination_address`. Processed manually by the Agentcard team, usually within 1-3 business days; the user is emailed at each step. Renamed from `withdraw_wallet` in July 2026. | ## Payment methods | Tool | Purpose | | ---------------------------- | --------------------------------------------------------------------- | | `setup_payment_method` | Save a payment method via Stripe (charged only for flight bookings). | | `list_payment_methods` | List saved methods (id, brand, last4, expiry); marks the default. | | `set_default_payment_method` | *Advanced.* Choose which saved method is charged for flight bookings. | | `remove_payment_method` | *Advanced.* Remove a saved payment method. | ## Plans | Tool | Purpose | | -------------- | -------------------------------------------------------------------- | | `get_plan` | Show current plan, per-card cap, monthly usage, and upgrade options. | | `upgrade_plan` | Start a Stripe Checkout to upgrade (Basic or Pro). | | `cancel_plan` | *Advanced.* Cancel the active paid subscription (revert to Free). | ## Rewards | Tool | Purpose | | ---------------- | ------------------------------------------------------------------------------- | | `get_rewards` | Show the tokenback balance and rate. See [Rewards](/personal/concepts/rewards). | | `redeem_rewards` | Redeem tokenback (1 token = 1 cent) as balance credit. | ## Shopping (`buy`) The conversational `buy` tool runs the whole shop-and-checkout flow; the granular `buy_*` tools back it and are hidden by default. See the [Shopping guide](/personal/shopping). | Tool | Purpose | | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | | `buy` | Natural-language shopping: describe a purchase and it runs the flow. | | `surprise_me` | Buy the user something fun under a dollar cap, through the same buy flow. | | `buy_list_merchants` | List merchants and link status. | | `buy_connect` / `buy_connect_status` | Link a merchant account with a hosted connect link, then poll until it lands. | | `buy_link_merchant` / `buy_confirm_merchant` / `buy_unlink_merchant` | Link (credential flow), confirm, or disconnect a merchant. | | `manage_subscription` | Manage a merchant subscription (e.g. skip a delivery, change a setting). | | `buy_search_stores` / `buy_select_store` | Find and scope to a store within a multi-store merchant. | | `buy_search_products` | Search a linked merchant for products. | | `buy_add_to_cart` / `buy_set_item_quantity` / `buy_remove_from_cart` / `buy_view_cart` / `buy_clear_cart` | Build and inspect the cart. | | `buy_set_budget` | Set a spend budget. | | `buy_checkout` | Place and pay for the cart. | | `buy_order_history` / `buy_reorder` / `buy_track_order` / `buy_set_substitution` | Review, re-create, track orders, and set out-of-stock preferences. | | `buy_addresses` / `buy_set_default_address` | List and set delivery addresses. | ## Settings | Tool | Purpose | | ----------------- | ---------------------------------------------------------------------------------------- | | `get_settings` | Show notification preferences, the default delivery address, and authorization settings. | | `update_settings` | *Advanced.* Change email notification preferences or the default delivery address. | ## Support | Tool | Purpose | | ---------------------- | -------------------------------------- | | `start_support_chat` | Open a new support conversation. | | `send_support_message` | Send a message in a conversation. | | `read_support_chat` | Read a conversation's message history. | ## Connected apps | Tool | Purpose | | ------------------- | ------------------------------------------------- | | `list_connections` | List third-party apps connected to your account. | | `revoke_connection` | Disconnect one of them (its tokens stop working). | # Payment Methods Source: https://docs.agentcard.sh/personal/payment-methods Saved payment methods pay for flight bookings — cards are funded from your balance A **payment method** is a real card on file (held securely by Stripe). It is used for exactly one thing: **paying for flight bookings**. A flight never mints a virtual card — the fare (plus the service fee) is charged to your saved payment method when the booking is placed. Payment methods do **not** fund virtual cards or your balance. Virtual cards draw on your **balance**, topped up with Apple Pay or Google Pay via `add_funds` (CLI: `agent-cards fund`) — see the [Quickstart](/personal/quickstart). ## How flight payment works When you book a flight, Agentcard places a hold on your default payment method for the fare + service fee, books the ticket, then captures the hold. If you have no saved payment method, the booking is refused with `payment_method_required` before any money moves. ## Manage your methods ```bash theme={null} agent-cards payment-method setup # save a method via Stripe agent-cards payment-method list # list methods, marks the default agent-cards payment-method default # set the default agent-cards payment-method remove --id ``` * `setup_payment_method` — returns a Stripe URL to save a card. * `list_payment_methods` — list methods and the current default. * `set_default_payment_method` — choose which method is charged for flight bookings. * `remove_payment_method` — remove a saved method. The **default** payment method is the one charged when you book a flight. See the CLI details in [Payment Methods (CLI)](/personal/cli/payment-methods). ## Common error If a flight booking returns **`payment_method_required`**, you have no saved method yet — run `setup_payment_method` (or `agent-cards payment-method setup`) first. Card creation never needs a payment method — if `create_card` reports a shortfall, add to your balance instead (`add_funds`). # Plans Source: https://docs.agentcard.sh/personal/plans Free, Basic, and Pro limits Your plan sets how many cards you can create each month and the maximum amount per card. | | **Free** | **Basic** | **Pro** | | --------------- | ------------ | --------- | --------- | | Price | \$0 | \$15/mo | \$100/mo | | Cards per month | 5 | 15 | 50 | | Max per card | \$50 | \$500 | \$1,000 | | Shopping orders | 1 (lifetime) | Unlimited | Unlimited | Everyone starts on **Free**. [Test cards](/personal/concepts/test-mode) (from an organization's sandbox integration) don't count toward these limits or your monthly usage. ## View your plan ```bash theme={null} agent-cards plan ``` Call `get_plan` to see your current plan, per-card cap, cards used and remaining this month, and renewal status. ## Upgrade ```bash theme={null} agent-cards plan upgrade basic # $15/mo agent-cards plan upgrade pro # $100/mo ``` Call `upgrade_plan` — it returns a Stripe Checkout URL. Complete payment in your browser and the plan updates automatically; confirm with `get_plan`. ## Cancel Cancel from the CLI with `agent-cards plan cancel`, or via the `cancel_plan` MCP tool. You revert to Free at the end of the billing period. ## When you hit a limit Card creation returns a clear error when you exceed your plan: * **`amount_exceeds_limit`** — the requested amount is above your per-card cap. Lower the amount or upgrade. * **`card_limit_reached`** — you've used your monthly card quota. Upgrade or wait until next month. Check `get_plan` (or `agent-cards plan`) to see exactly where you stand. Need a higher limit than Pro? [Contact support](/personal/support). # Quickstart Source: https://docs.agentcard.sh/personal/quickstart Sign up, add cash to your balance, and create your first card This walks through the full Personal flow — from zero to a working virtual card your agent can use. ## Step 1: Install the CLI and sign in The `agent-cards` CLI is the fastest way to get started. Install it and sign up — we email you a one-time code to verify your address: ```bash theme={null} npm install -g agent-cards agent-cards signup ``` Enter the code from your email to finish signing in. Check who you're signed in as any time with `agent-cards whoami`. Prefer to drive everything from your agent? Skip to [Connect the MCP server](/personal/mcp/overview) — the same flow works through MCP tools. **Working from a coding agent?** One command teaches Claude Code, Codex, or Gemini to drive Agentcard themselves: ```bash theme={null} npx -y agent-cards@latest agents add ``` ## Step 2: Add cash to your balance Virtual cards are funded from your **cash balance**. Add funds with Apple Pay or Google Pay via a secure checkout link: ```bash theme={null} agent-cards fund --amount 50 ``` Open the returned URL and finish paying. Check the balance any time with: ```bash theme={null} agent-cards wallet ``` ## Step 3: Create your first card Amounts are in **dollars** (not cents): ```bash theme={null} agent-cards cards create --amount 25 ``` The CLI confirms the amount, then returns the card ID and summary. Pass `--yes` to skip the prompt or `--json` for non-interactive / agent use. Show the full number, CVV, and expiry when you need to fill a form: ```bash theme={null} agent-cards cards details ``` Ask your agent to create a card. Under the hood it calls `create_card` with `amount_cents` (so \$25 = `2500`). The first time, it will: 1. Call `add_funds` if your balance is short (it returns a checkout link to pay with Apple Pay / Google Pay). The first fund asks for a one-time verification code — your agent sends it with `start_phone_verification` and checks it with `verify_phone`; it stays valid for 60 days. 2. Walk you through one-time identity verification (`start_kyc`) — see [KYC & approvals](/personal/kyc-and-approvals). 3. Issue the card and return the last 4, balance, and expiry. Cards are **live**: each one draws on your cash balance when used. If a card ever comes back marked TEST (number starting `4242…`), it came from a connection that isn't live, usually a company's test-mode integration. See [Test cards](/personal/concepts/test-mode) for how to fix that. ## Step 4: Use the card * **Manually** — Read the PAN/CVV/expiry from `cards details` and enter them at checkout. * **Shop end-to-end** — Use the [`buy`](/personal/shopping) surface to order from a linked merchant in natural language. ## Step 5: Monitor and close ```bash theme={null} agent-cards balance # quick balance check agent-cards cards transactions # transaction history agent-cards cards close # close and release held funds ``` ## Next steps Connect Agentcard to your agent. Every `agent-cards` command. `agent-cards agents add` wires Claude Code, Codex, and Gemini; `agent-cards api` is the tool catalog. Raise your limits with Basic or Pro. Let your agent order from merchants. # Shopping Source: https://docs.agentcard.sh/personal/shopping Let your agent order from merchants end-to-end with buy The `buy` surface lets your agent shop at linked merchants and pay with a virtual card — all in natural language. It's the entire shopping flow: search, cart, budget, and checkout. There are no separate search/cart/checkout tools to wire up; `buy` is conversational and runs the whole flow. ## Supported merchants `rappi`, `goodeggs`, and `doordash`. DoorDash is multi-store, so you can scope to a specific store before searching. ## Use it from your agent With the [MCP server](/personal/mcp/overview) connected, describe what you want: > "Order a caesar salad from Zuni on DoorDash." The `buy` tool asks for anything it needs (delivery address, which store), builds the cart, confirms the items and total, and only places the order after you explicitly confirm. Call `buy` again to answer its questions or to place the order. Save a default delivery address once — in the dashboard (Settings → Delivery address), with `agent-cards settings address`, or by telling your agent to save it (`update_settings`) — and agents confirm that address instead of asking you to dictate one on every order. Merchants where your linked account already has saved addresses keep using the account's own default. ## Use it from the CLI ```bash theme={null} agent-cards buy the caesar salad from Zuni on doordash ``` or open the chat with a bare `agent-cards buy`. The granular subcommands (`buy link`, `buy search`, `buy add`, `buy checkout`, …) are documented in the [Buy CLI reference](/personal/cli/buy). ## Linking a merchant Before shopping, link the merchant account once: * **One-time code:** `buy link …` then `buy confirm --pending --code `. * **Hosted browser login (DoorDash):** `buy connect doordash`. ## Budgets and safety * Set a spend cap with `buy budget ` (`daily`, `weekly`, `monthly`, or `total`). * Checkout is destructive and always requires explicit confirmation (`--yes` on the CLI). * Use `--idempotency-key` on retries so a timed-out checkout never double-charges. * On **Free**, you get 1 lifetime order; Basic and Pro are unlimited (see [Plans](/personal/plans)). ## After ordering ```bash theme={null} agent-cards buy orders # order history agent-cards buy track # status + ETA agent-cards buy reorder # re-create a past cart ``` Set out-of-stock handling per item with `buy substitution `. # Support Source: https://docs.agentcard.sh/personal/support Talk to Agentcard support Reach a human through a live support conversation, from the CLI or through your agent. ```bash theme={null} agent-cards support # start a conversation agent-cards support --resume # resume an existing one ``` * `start_support_chat` — open a new conversation; save the returned `conversation_id`. * `send_support_message` — send a message in a conversation. * `read_support_chat` — read the conversation's message history for replies. You can also email [support@agentcard.sh](mailto:support@agentcard.sh). ## Higher limits Need more than Pro's 50 cards/month or \$1,000 per card? Start a support conversation and ask — see [Plans](/personal/plans) for the standard tiers. # Choose your platform Source: https://docs.agentcard.sh/platforms/choose-your-platform One wallet, five hosts. Pick where your product lives and follow one page. The wallet is one hosted component with five ways to show it. Your server creates a wallet link either way; the platform only changes how the wallet opens. Pick the row that matches your product and follow that one page. | Your product | Use | Page | | ------------------ | ------------------------------------- | --------------------------------------- | | A web app | The JavaScript embed | [Web](/platforms/web) | | An iOS app | The Swift package | [iOS](/platforms/ios) | | A React Native app | The React Native component | [React Native](/platforms/react-native) | | An iMessage agent | A texted link, opened by the App Clip | [iMessage](/platforms/imessage) | | No frontend at all | The hosted page | [Hosted link](/platforms/hosted-link) | If you're not sure, start with [Hosted link](/platforms/hosted-link). It needs nothing but a URL, and you can switch to an embedded platform later without changing anything on your server. The wallet handles adding cards, viewing them, paying, and identity verification when issuing needs it. Adding balance opens on hosted pages we run, so it works the same on every platform. # Hosted link Source: https://docs.agentcard.sh/platforms/hosted-link No frontend? Create a wallet link and send the URL. We host the whole wallet. This is the wallet for products with no frontend: CLIs, backend agents, email flows, anything that can deliver a URL. You create a wallet link and hand the URL to the user however you like. We host the page it opens. ## Create a wallet link ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response looks like this: ```json theme={null} { "object": "wallet_link", "id": "wl_...", "user_id": "USER_ID", "status": "active", "url": "https://app.agentcard.sh/w/...", "expires_at": "2026-08-11T20:15:00.000Z", "test_mode": true } ``` ## Deliver the URL Print it in the terminal, put it in an email, drop it in a chat. The user opens it in any browser and gets the full wallet: add a card, see their cards, approve a payment. ## Link semantics A link belongs to one user and expires after 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). It can be opened up to 20 times within that window, so a user can tap it again without asking for a new one. Link previews and unfurl bots never consume an open; the page only exchanges the link when a real browser loads it. When the user needs the wallet again later, create a fresh link. They are cheap and there's no limit on how many you create. ## Webhooks you will receive * `wallet_link.opened` the first time the link is opened * `connected_card.updated` when the user adds a card * `transaction.*` events when payments happen Don't poll the link for status. The webhooks are the record of what actually happened. ## When it fails An expired link shows the user an expired screen. Every other refusal shows one generic screen asking them to request a fresh link. Your handling is one code path: create a new link and send it. ## Sandbox behavior With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves. Next: [Connect users](/wallet/connect-users) # iMessage Source: https://docs.agentcard.sh/platforms/imessage Give an iMessage agent a wallet by texting a link. Tapping it opens the wallet, nothing to install. This is the wallet for agents that live in iMessage. There's nothing to embed and no Messages extension. Your agent texts the user their wallet link, and tapping it opens their wallet. ## Create a wallet link (server side) ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response includes a `url` like `https://app.agentcard.sh/w/...`. That URL is the whole integration. ## Text the link Send the URL in the conversation, worded however your agent talks: > Here's your wallet, add a card and I can start buying for you: > [https://app.agentcard.sh/w/](https://app.agentcard.sh/w/)... Links are safe to text. Messaging apps that unfurl previews don't consume the link, because the preview fetch never exchanges it. ## What the user sees The link opens the hosted wallet page: they add a card or approve a payment, then they're back in the conversation. It works on every device, and there's nothing to install. One link survives multiple taps. Each open exchanges it for a short session, and a link allows up to 20 opens inside its lifetime, 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). For a returning user, have your agent create a fresh link at the moment it's needed rather than reusing old ones. A native App Clip version of the wallet ships with our iOS app: the same links will open it directly on iPhone, and nothing about your integration changes when it does. ## Webhooks you will receive * `wallet_link.opened` the first time the user opens the link * `connected_card.updated` when they add a card * `transaction.*` events when payments happen Your agent should react to webhooks, not to the conversation. The user saying "done" is a claim; `connected_card.updated` is a fact. ## When it fails An expired link shows the user an expired screen, and every other refusal (revoked, used up, wrong device state) shows one generic screen that tells them to ask for a fresh link. Either way the fix is the same: create a new wallet link and text it. ## Sandbox behavior With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves. Next: [Connect users](/wallet/connect-users) # iOS (Swift) Source: https://docs.agentcard.sh/platforms/ios Add the Agentcard wallet to your iOS app with the AgentcardWalletKit Swift package. This is the wallet for native iOS apps. AgentcardWalletKit is a SwiftUI package: you pass it a wallet link and present it like any other view. It talks to the API natively, and only the card entry step runs in a secure web view. ## Install (Swift Package Manager) In Xcode, add the package: ``` https://github.com/tiny-agent-company/agentcard-wallet-ios ``` Pin it at `0.1.0` or later. The repository is private while the SDK is in early access, so ask us for an invite and we'll add your GitHub account. ## Create a wallet link (server side) ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response includes a `url`. Hand it to your app. Links expire after 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). ## Present the wallet ```swift theme={null} import AgentcardWalletKit .sheet(isPresented: $showWallet) { AgentcardWalletSheet(link: walletLinkURL) { event in switch event { case .cardAttached(let cards): // the user added a card case .paymentCompleted(let merchant, let amountCents): // a payment went through case .tokenExpired: // fetch a fresh wallet link and present again default: break } } } ``` `AgentcardWalletSheet` also accepts `linkToken:` if you'd rather pass the raw token than the URL. ## Ask for a payment Pass a pay intent to open the pay sheet instead of the wallet home. `reference` is required: it's your stable id for this payment, and it's what makes retries safe. ```swift theme={null} AgentcardWalletSheet( link: walletLinkURL, pay: AgentcardPayIntent(merchant: "Amazon", amountCents: 500, reference: order.id) ) { event in ... } ``` ## Webhooks you will receive Treat the Swift events as UI signals and let your server trust webhooks: * `connected_card.updated` when the user adds a card * `transaction.*` events when payments happen ## When it fails The sheet handles failures itself and tells the user what to do, so there is one case your code handles: `.tokenExpired`, which fires when the link or its session dies. Create a fresh wallet link and present the sheet again. ## Sandbox behavior With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves. Next: [Connect users](/wallet/connect-users) # React Native Source: https://docs.agentcard.sh/platforms/react-native Add the Agentcard wallet to your React Native app with @agentcard/wallet-react-native. This is the wallet for React Native apps. It's one component: you give it a wallet link, it renders the wallet, and you get the same events the web SDK fires. ## Install ```bash theme={null} npm install @agentcard/wallet-react-native react-native-webview ``` The package is private while the SDK is in early access, so the install fails until your team has access. Write to us and we'll set you up. ## Create a wallet link (server side) ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response includes a `url`. Hand it to your app. Links expire after 15 minutes by default (`expires_in` accepts 60 to 86400 seconds). ## Render the wallet ```tsx theme={null} import { AgentcardWallet } from "@agentcard/wallet-react-native"; { // data.type is "card_attached" or "payment_completed" }} onTokenExpired={() => { // fetch a fresh wallet link from your server and swap the `link` prop }} /> ``` To ask for a payment instead of opening the wallet home: ```tsx theme={null} {}} /> ``` The component accepts `link` as either the full URL or the raw token. `onEvent(name, data)` receives every bridge event if you want analytics, and `style` sizes the view. ## Webhooks you will receive Component events are UI signals. Your server should trust webhooks: * `connected_card.updated` when the user adds a card * `transaction.*` events when payments happen ## When it fails When the link or its session dies, `onTokenExpired` fires: create a fresh wallet link and swap the `link` prop. Bank approvals open in the system browser by default; override that with `onOpenUrl(url)` if you want an in-app browser. Passkey prompts need iOS 17 or later inside a WebView, so older devices fall back to code verification. ## Sandbox behavior With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves. Next: [Connect users](/wallet/connect-users) # Web Source: https://docs.agentcard.sh/platforms/web Add the Agentcard wallet to any web app with a script tag and one call. This is the wallet for web apps. You load one script, pass it a wallet link, and the wallet opens as a sheet over your page. Card entry happens inside our iframe, so card numbers never touch your code. ## Register your web origins The embed only opens on origins you've registered. Register them once from your server: ```bash cURL 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"]}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/embed_origins", { method: "PUT", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ origins: ["https://app.example.com", "http://localhost:3000"] }), }); const data = await res.json(); ``` ```python Python theme={null} import requests res = requests.put( "https://api.agentcard.sh/api/v2/embed_origins", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"origins": ["https://app.example.com", "http://localhost:3000"]}, ) data = res.json() ``` `PUT` replaces the whole list and `GET` reads it back. Up to 10 origins, `https` only, except that `http` is allowed for `localhost`, `127.0.0.1`, and `[::1]` while you develop. Values are normalized to their origin (scheme, host, port), and changes take effect within about five minutes. On an unregistered origin the wallet doesn't open at all, so this is the first thing to check if you see a blank frame. ## Create a wallet link (server side) The wallet needs a link for the user who is opening it. Create one from your server: ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response includes a `url`. Hand that to your frontend. Links expire after 15 minutes by default, and you can pass `expires_in` (60 to 86400 seconds) to change that. ## Open the wallet Your backend creates the wallet link; your page fetches it from you and hands it to the wallet: ```html theme={null} ``` Load the script from our URL. Don't bundle it; that's how you keep getting fixes without shipping updates. To ask for a payment instead of opening the wallet home, pass `pay`: ```js theme={null} AgentCard.create({ token: link.url, pay: { amountCents: 500, merchant: "Amazon" }, onSuccess: (data) => {}, }).open(); ``` The other methods: `update(next)` swaps the token or pay intent in place, `exit()` closes the sheet, and `destroy()` removes everything. Every bridge event also reaches `onEvent(name, data)` if you want analytics. ## Webhooks you will receive The SDK events are UI signals. Your server should trust webhooks instead: * `connected_card.updated` when the user adds a card * `transaction.authorized` and the other `transaction.*` events when payments happen ## When it fails If the wallet link expired, the sheet says so and `onTokenExpired` fires. Create a fresh link and call `wallet.update({ token })`. Bank approvals sometimes need a popup. If the browser blocks it, the SDK shows an approval bar inside the sheet that the user can tap instead. You can take over that behavior with `onOpenUrl(url)`. ## Sandbox behavior With sandbox credentials, the connect code is always `111111` and the test card is `4242 4242 4242 4242` with any future expiry and any CVC. No real money moves. Next: [Connect users](/wallet/connect-users) # Purchase API Source: https://docs.agentcard.sh/purchase/purchase-api Agents send one call with plain text, and we place the real order. This is how agents actually buy things. A card alone doesn't finish a checkout. Someone still has to find the merchant, log in, and place the order, and that's what the Purchase API does. It's one endpoint: the agent sends plain text, and we find the merchant, build the cart, and place the order with the user's card. ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/buy \ -H "Authorization: Bearer $BUY_TOKEN" \ -H "Content-Type: application/json" \ -d '{"ask": "two boxes of Pampers size 4 from Amazon"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/buy", { method: "POST", headers: { Authorization: `Bearer ${BUY_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ ask: "two boxes of Pampers size 4 from Amazon" }), }); const result = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/buy", headers={"Authorization": f"Bearer {BUY_TOKEN}"}, json={"ask": "two boxes of Pampers size 4 from Amazon"}, ) result = res.json() ``` The same call is the `buy` tool on the MCP server. Same behavior, same states. ## Where agents can buy Amazon and more than a dozen other large retailers, DoorDash, Uber Eats, Walmart, Sephora, TaskRabbit, Rinse, Good Eggs, Rappi, Locale, and flights. The list grows; `buy` itself tells the agent when a merchant needs the user to link their account first. ## How a purchase actually flows Every response has a `status`, and there are only four: | Status | What it means | | ------------------ | -------------------------------------------------------------------------------- | | `needs_input` | The `reply` is a question. Send the answer back with the same `conversation_id`. | | `order_placed` | The order is in. The `reply` carries the confirmation. | | `partially_placed` | A multi-merchant confirm placed some orders; each cart reports its own outcome. | | `declined` | We refused: over budget, unsupported merchant, or a failed check. | Money never moves on a plain `ask`. When a cart is ready, the response includes the cart with an exact total and a `hash`. To place the order, the agent sends the hash back: ```json theme={null} { "conversation_id": "...", "confirm": "9f2c4a1b8e3d5f07" } ``` The hash covers the exact items and total that were shown. If anything about the cart changed since, the confirm fails safely instead of charging something the user never saw. ## Give an agent access Create a buy token for the user. It lasts 30 days and only works as that one user: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v1/cardholders/CARDHOLDER_ID/buy_token \ -H "Authorization: Bearer $ORG_TOKEN" ``` ```javascript Node theme={null} const res = await fetch( "https://api.agentcard.sh/api/v1/cardholders/CARDHOLDER_ID/buy_token", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}` } }, ); const buyToken = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v1/cardholders/CARDHOLDER_ID/buy_token", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, ) buy_token = res.json() ``` The same thing is available as `mint_buy_token` on [MCP](/tools/mcp) when connected as your organization. Hand the token to the agent; it's the bearer for every `buy` call. ## What it costs Purchases carry a service fee, 2.5% of the merchant total, shown to the user in the cart before any confirm. The totals in `needs_input` replies are all-in: what the user sees is what the card is charged. ## Spending controls and approvals Budgets cap what an agent can spend. Cards can be locked to one merchant or one purchase. And when a purchase needs a human, the user gets an approval prompt and you receive `approval.requested`; the purchase waits for the yes. ## Webhooks you will receive * `transaction.authorized`, then `transaction.cleared` as the payment settles * `approval.requested` when a purchase is waiting on the user * `merchant.connected` when a user links a merchant account ## When it fails A `declined` status always says why in the `reply`. A confirm with a stale hash returns a conflict instead of charging; re-ask to get a fresh cart. If the user has no usable card, `buy` says so and the fix is the wallet, not the Purchase API. ## Sandbox behavior Sandbox conversations run the same loop with no real orders and no real money. Use the org server's `test_charge` to watch settlement events end to end. Next: [Test in sandbox](/ship/test-in-sandbox) # Errors and troubleshooting Source: https://docs.agentcard.sh/ship/errors-and-troubleshooting The error shape, what each error means, and what to send us if you're stuck. ## The error shape Every `/api/v2` error looks like this: ```json theme={null} { "error": { "code": "user_info_required", "message": "Recorded consent is required before creating a wallet link.", "docs": "https://docs.agentcard.sh" } } ``` `code` is stable and worth branching on; `message` is for humans and can change. Some older surfaces still return a flat `{ "error": "...", "message": "..." }` shape. If you're on `/api/v2`, you'll always get the nested one. ## Check these first If something isn't working, run down this list in order. **1. The credential and the call.** An org token where a connection token belongs, or a wallet link treated like a session. The [keys and tokens table](/wallet/connect-users#keys-and-tokens) settles it; every credential has exactly one place it goes. **2. Hand-building flows the wallet already runs.** If you're implementing card entry, identity verification, or balance screens against the API, stop. The wallet does all of it, and the API-only versions exist for a different kind of product. Read [How the wallet works](/wallet/how-the-wallet-works) before writing more code. **3. `user_info_required` when creating wallet links.** Consent was never recorded for that user. Call `POST /api/v2/connect/consent` once after the user verifies, then create links freely. **4. `subscription_required` (402) in production.** Production credentials act only while a subscription is active. Sandbox never needs one. See [Go live](/ship/go-live). **5. Testing blind in sandbox.** Sandbox never tells you its shortcuts exist. The code is `111111`, the test card is `4242 4242 4242 4242`, and everything else is on [Test in sandbox](/ship/test-in-sandbox). **6. A funding model that won't change.** User-funded versus company balance locks at your first live transaction. If you need to change it after that, talk to us. ## Webhook signatures failing Almost always one of two things: you verified against a re-serialized body instead of the raw bytes, or you used another endpoint's secret. The verification recipe is on [Webhooks](/wallet/webhooks#verify-the-signature). ## Still stuck Write to [support@agentcard.sh](mailto:support@agentcard.sh) with four things: the endpoint you called, which credential type you used, what you expected, and what you got, with the error `code`. That's usually enough for a same-day answer. If your coding agent is doing the integration, connect the [MCP server](/tools/mcp) and let it debug with you. # Go live Source: https://docs.agentcard.sh/ship/go-live Here are the four steps between a working sandbox integration and real money. If your integration works in sandbox, it works in production. Going live is four steps, not a migration. ## The checklist 1. **Subscribe.** In the [dashboard](https://app.agentcard.sh) or with `agent-cards companies subscribe`. The subscription is what unlocks production. 2. **Create production credentials.** A production `client_id` and `client_secret` from **Settings → Developers → Credentials**. Nothing else in your code changes; the base URL stays `api.agentcard.sh`. 3. **Point webhooks at production.** Create a production webhook endpoint and verify signatures against its own secret. Sandbox and production endpoints are separate. 4. **Run the loop once with real money.** Connect a real user, add a real card, make a small real purchase. Watch the same webhooks you saw in sandbox arrive with `livemode: true`. ## The gate fires at use time You can create production credentials before subscribing, and they will authenticate. What they can't do is act: calls that create wallet links, add cards, or issue cards fail with `subscription_required` (HTTP 402) until the subscription is active. If production suddenly returns 402 where sandbox worked, this is why. ## What changes with real money * Identity verification is real: documents and a face check, no simulate endpoint. * Card eligibility is enforced for real. Almost any credit or debit card works; the known exception today is Chase. * Balance funding uses real payment providers, including ones sandbox can't simulate. * The one-time code is never `111111` again. Next: [Errors and troubleshooting](/ship/errors-and-troubleshooting) # Test in sandbox Source: https://docs.agentcard.sh/ship/test-in-sandbox Every test knob in one place. Sandbox looks exactly like production, so this page is where the knobs become visible. There is one API, `api.agentcard.sh`. Sandbox is not a different URL; it's what you get when you authenticate with sandbox credentials. Everything behaves the same, except nothing is real: no emails, no SMS, no money. The catch is that sandbox looks so much like production that its shortcuts are invisible. API responses never mention them. Here they all are. ## The knobs | Knob | What it does | | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | Code `111111` | The one-time code for everything: connecting users, phone verification. Sandbox never sends a real code, and this one always works. | | Card `4242 4242 4242 4242` | Adds instantly in the wallet with any future expiry and any CVC. | | Card `4242 4300 0000 0017` | Runs the full real add ceremony against the test network, if you want to see every step. | | `POST /api/v2/kyc/simulate` | Sets a user's verification outcome: `approved`, `rejected`, or `requires_input`. Returns 403 outside sandbox. | | `agent-cards companies balance test-fund` | Adds sandbox balance so you can issue Agentcards and spend. | | `test_charge` | On [MCP](/tools/mcp), connected as your organization: simulates a full charge against a card, with authorization, settlement, and every webhook. | ## Sandbox users are isolated Any email or phone works in sandbox, including real ones, and it can never touch a real account. Sandbox identities live in their own namespace, so `ceo@yourcustomer.com` in sandbox is not, and can never become, that person's account. ## What has no sandbox A few balance funding providers are production-only, so end-to-end onramp payments can't be simulated. Use `test-fund` for balance in sandbox and verify the real funding flow with a small amount when you go live. ## Simulate a full flow This is the whole product in six sandbox steps, most of it covered by the [Quickstart](/get-started/quickstart): 1. Connect a user with `111111`. 2. Create a wallet link and open the wallet. 3. Add `4242 4242 4242 4242`, and watch `connected_card.updated` arrive. 4. Simulate KYC approval, `test-fund` some balance, and issue an Agentcard. 5. Run `test_charge` against it, and watch `transaction.authorized` and `transaction.cleared` arrive. 6. Check the deliveries log in the dashboard. That's every event your production endpoint will need to handle. Next: [Go live](/ship/go-live) # CLI Source: https://docs.agentcard.sh/tools/cli Run your Agentcard organization from the terminal: credentials, test balance, webhooks, and the integration wizard. Everything you can do in the dashboard, you can do from the terminal. The same CLI your users run has a `companies` namespace for organizations. ## Installation ```bash theme={null} npm install -g agent-cards ``` Update any time with `agent-cards update`. ## Authentication ```bash theme={null} agent-cards login ``` A sign-in link and short code appear, and you approve in your browser. Then pick the organization you work in: ```bash theme={null} agent-cards companies list agent-cards companies use ORG_ID ``` For scripts and CI, `agent-cards login --email you@example.com` sends a one-time code, and adding `--code 123456` completes it without a browser. ## Commands All of these live under `agent-cards companies`: | Command | What it does | | ------------------------------------------------------ | ----------------------------------------------------------------------------- | | `create` / `list` / `get` / `use` | Create an organization, list yours, pick the active one | | `wizard` | The integration agent: implements Agentcard in your codebase and reports back | | `credentials create` / `list` / `revoke` | Manage the OAuth clients behind your `client_id` and `client_secret` | | `env test` / `env production` | Point the CLI at sandbox or production credentials | | `balance get` / `provision` / `transfers` / `settings` | See and manage the company balance | | `balance test-fund` | Add sandbox balance so you can test issuing end to end | | `balance withdraw` | Withdraw from the company balance | | `webhooks list` / `create` / `update` / `delete` | Manage webhook endpoints | | `webhooks reveal` / `roll-secret` | Read or rotate an endpoint's signing secret | | `webhooks deliveries` / `test` | See recent deliveries, or send a test event | | `members` | Manage who is in the organization | | `subscribe` | Start the subscription that unlocks production | | `return-urls` | Set where hosted flows send users back to | Use `--help` on any of them for flags: ```bash theme={null} agent-cards companies webhooks test --help ``` ## Good to know * Sandbox or production follows the credentials you're using, the same rule as the API. * `webhooks test` is the fastest way to check your endpoint verifies signatures correctly before going live. * The wizard reads these docs as its source. If you'd rather integrate by hand, nothing here is required: the [Quickstart](/get-started/quickstart) uses plain curl. Next: [Test in sandbox](/ship/test-in-sandbox) # Dashboard Source: https://docs.agentcard.sh/tools/dashboard The web console for your organization: credentials, webhooks, users, transactions, and earnings, with a Live/Test toggle. Everything the CLI and MCP can do, you can also do by clicking. The dashboard lives at [app.agentcard.sh](https://app.agentcard.sh), and the Live/Test toggle in the top bar decides which mode you're looking at, the same way your credentials decide it for the API. ## Where things are | Section | What you do there | | ----------------------------------------------- | ------------------------------------------------------------------ | | **Settings → Developers → Credentials** | Your `client_id` and `client_secret`, per mode, with rotation | | **Settings → Developers → Implement Agentcard** | Create your app and get integration guidance | | **Settings → Developers → Webhooks** | Endpoints, signing secrets, and the delivery log with payloads | | **Settings → Developers → Logs** | Every API request your credentials made, for debugging | | **Users** | Your connected users, their wallets, and their verification status | | **Transactions** | Everything your users' agents have spent | | **Cards** | The cards behind those transactions | | **Wallet** | Your company balance, transfers, and withdrawals | | **Earnings** | Your interchange share and markup revenue | | **Settings → Revenue** | Configure your markup and reward routing | | **Settings → Billing** | The subscription that unlocks production | ## When to reach for it The dashboard is where you look when something needs eyes: a webhook delivery that failed, a user stuck in verification, a transaction you want to trace. During the [Quickstart](/get-started/quickstart) it's how you watch your first events arrive, and on the way to production it's where you [subscribe and create live credentials](/ship/go-live). Next: [Test in sandbox](/ship/test-in-sandbox) # MCP Source: https://docs.agentcard.sh/tools/mcp One MCP server for everything: manage your organization from your coding agent, and give your users' agents a way to buy. ## Install the Agentcard MCP ```bash theme={null} npx -y agent-cards setup-mcp ``` One command. A browser sign-in appears the first time a tool runs, and new emails get an account on the spot. If you don't use Claude Code, it prints the server URL to add in any MCP client. There is one Agentcard MCP server, at `https://mcp.agentcard.sh/mcp`. What it exposes depends on the credential you connect with: your own account, your organization, or one of your users. ## Connect as your organization You can run your whole Agentcard integration without leaving your coding agent. Connect with the same credentials you use for the API: ```bash theme={null} claude mcp add agentcard --transport http https://mcp.agentcard.sh/mcp \ --header "Authorization: Bearer YOUR_CLIENT_SECRET" ``` That works in any MCP client. For Cursor or Claude Desktop, add the same URL and header to your MCP config. Whether you get sandbox or production is decided by the credential you connect with, like everywhere else. ## What your agent can do with it 22 tools, the same operations as the API. Your agent can build the integration, test it, and debug it in production. | Area | Tools | | --------------- | -------------------------------------------------------------------------------------------------------------------- | | Users | `create_cardholder` · `list_cardholders` · `get_cardholder` · `create_onboarding_session` · `get_onboarding_session` | | Identity | `start_cardholder_kyc` · `get_cardholder_kyc_status` | | Cards | `create_card` · `list_cards` · `get_card` · `get_card_details` · `close_card` | | Payment methods | `setup_cardholder_payment_method` · `get_cardholder_payment_method_status` | | Company balance | `get_company_wallet` · `list_transfers` · `confirm_collection` · `recover_funds` · `list_recoveries` | | Testing | `test_charge` (simulates a full charge in sandbox: authorization, settlement, webhooks) | | Session | `whoami` · `mint_buy_token` | When something breaks during your integration, connect this server and ask your agent to debug it. It can read the same state we see. ## Give your users' agents a way to buy The organization server is for you. Your users' agents connect to a different server, as the user. Create a buy token for a user with `mint_buy_token` (or `POST /api/v1/cardholders/:id/buy_token`). It lasts 30 days and only works for that one user. The agent then connects with it: ```bash theme={null} claude mcp add agentcard-user --transport http https://mcp.agentcard.sh/mcp \ --header "Authorization: Bearer BUY_TOKEN" ``` That agent gets the user's tools, and the one that matters is `buy`. The whole story is on the [Purchase API](/purchase/purchase-api) page. Next: [Test in sandbox](/ship/test-in-sandbox) # Connect users Source: https://docs.agentcard.sh/wallet/connect-users Authenticate your server, connect a user, and get the wallet link that opens their wallet. Before a user can have a wallet in your product, two things happen: your server proves who it is, and the user proves who they are. Both take one call each. This page is the server side of the whole integration. ## Keys and tokens There are only three credentials in the system, and each one has exactly one place it goes. | Credential | What it is | Who holds it | Where it goes | | -------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------- | -------------------------------------- | | **Org credential** | Your `client_id` and `client_secret`, exchanged for a bearer token | Your server only. Never a browser, never an agent | Every `/api/v2` call your server makes | | **Wallet link** | A short-lived URL that opens one user's wallet | Travels to the user: through your frontend, a text, or a chat | The wallet component, on any platform | | **Connection token** | One user's session, with a refresh token | Your server stores and refreshes it | Calls made on behalf of that user | The pattern to remember: your org credential stays on your server. The wallet link is the only thing that travels to the user. The connection token comes back when a user verifies, and your server keeps it. ## Authenticate your server Exchange your credentials for a bearer token. The request is form-encoded, per OAuth: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/oauth/token \ -d grant_type=client_credentials \ -d client_id=YOUR_CLIENT_ID \ -d client_secret=YOUR_CLIENT_SECRET ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/oauth/token", { method: "POST", body: new URLSearchParams({ grant_type: "client_credentials", client_id: "YOUR_CLIENT_ID", client_secret: "YOUR_CLIENT_SECRET", }), }); const { access_token } = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/oauth/token", data={ "grant_type": "client_credentials", "client_id": "YOUR_CLIENT_ID", "client_secret": "YOUR_CLIENT_SECRET", }, ) access_token = res.json()["access_token"] ``` You get back an `access_token` with an `expires_in`. Cache it and refresh when it expires. Whether it's a sandbox or production token follows the credential, not the URL. ## Connect a user Send the user a one-time code, on email or phone: ```bash cURL 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", "external_user_id": "your-internal-id"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/start", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ email: "user@example.com", external_user_id: "your-internal-id" }), }); const attempt = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/start", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"email": "user@example.com", "external_user_id": "your-internal-id"}, ) attempt = res.json() ``` `external_user_id` is optional. It's your own id for the user, and it comes back on webhooks so you can match them up. Verify the code the user gives you. In sandbox the code is always `111111`: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/connect/verify \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"connect_id": "CONNECT_ATTEMPT_ID", "code": "111111"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/verify", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ connect_id: "CONNECT_ATTEMPT_ID", code: "111111" }), }); const connection = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/verify", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"connect_id": "CONNECT_ATTEMPT_ID", "code": "111111"}, ) connection = res.json() ``` The response is the connection: the user's `user_id`, an `access_token` (the connection token), and a `refresh_token`. Store all three. The `connection.created` webhook fires here. Then record the user's authorization once: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/connect/consent \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"user_id": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/consent", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const data = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/consent", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) data = res.json() ``` Consent is what makes the user's wallet available to your product. Wallet links won't be created without it. ## Create wallet links With a connected, consented user, your server can create wallet links whenever the wallet should open: ```bash cURL 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": "USER_ID"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/wallet_links", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ user_id: "USER_ID" }), }); const link = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/wallet_links", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"user_id": "USER_ID"}, ) link = res.json() ``` The response has a `url`. That link opens the user's wallet on any platform: the web embed, the iOS sheet, a text message, or the hosted page. Links are short-lived; create a fresh one each time rather than storing them. ## Keep the session alive Connection tokens expire. Rotate them with the refresh token: ```bash cURL theme={null} curl -X POST https://api.agentcard.sh/api/v2/connect/refresh \ -H "Authorization: Bearer $ORG_TOKEN" \ -H "Content-Type: application/json" \ -d '{"refresh_token": "REFRESH_TOKEN"}' ``` ```javascript Node theme={null} const res = await fetch("https://api.agentcard.sh/api/v2/connect/refresh", { method: "POST", headers: { Authorization: `Bearer ${ORG_TOKEN}`, "Content-Type": "application/json", }, body: JSON.stringify({ refresh_token: "REFRESH_TOKEN" }), }); const session = await res.json(); ``` ```python Python theme={null} import requests res = requests.post( "https://api.agentcard.sh/api/v2/connect/refresh", headers={"Authorization": f"Bearer {ORG_TOKEN}"}, json={"refresh_token": "REFRESH_TOKEN"}, ) session = res.json() ``` Each refresh returns a new pair and invalidates the old one, so store what comes back. ## Webhooks you will receive * `connection.created` when a user verifies, with your `external_user_id` on it ## When it fails * An auth error usually means the wrong credential for the call. Check the table at the top. * `user_info_required` on wallet links means consent was never recorded for that user. Call `/connect/consent`. * `subscription_required` in production means your organization hasn't subscribed yet. Sandbox never requires one. * `invalid_refresh_token` means the refresh token was already used or expired. Reconnect the user. ## Sandbox behavior Sandbox never sends a real email or SMS, and the code is always `111111`. Sandbox users are isolated: connecting `anyone@example.com` in sandbox can never touch a real account with that email. Next: [Webhooks](/wallet/webhooks) # How the wallet works Source: https://docs.agentcard.sh/wallet/how-the-wallet-works Everything the Agentcard wallet handles for you, and the webhook that confirms each step. The Agentcard wallet stores your users' credit and debit cards, along with the Agentcards we issue, so their agents can use them securely. It's a component you put in your product, and it runs every card flow itself. You don't implement any of what's on this page. You just get told about it through webhooks. ## Adding a card A user opens the wallet and puts in a card they already have. No identity verification, no balance. Three steps, and the wallet runs all of them. ### 1. The user consents The first time, the wallet asks the user to authorize their agent to use the cards they add. One tap, recorded on our side. ### 2. The card is checked The user enters the card and we run the ceremony with the card's network. Almost any credit or debit card works; the known exception today is Chase, which doesn't yet allow this kind of enrollment. When a card doesn't qualify, the wallet explains it to the user and offers to use a different card, so you don't have to handle it. ### 3. The card is in the wallet The card is enrolled and the agent can use it. The moment that happens, you receive `connected_card.updated`. ## Issuing Agentcards The other way a card gets into the wallet is that we issue one. An Agentcard is a card we create for the user, backed by balance: single use or multi use, locked to a merchant, capped to an amount. It can never spend more than the balance behind it. Issuing takes three steps, and the wallet runs all of them. ### 1. The user verifies their identity Identity verification only happens for issuing. It never happens for adding their own card. It runs right inside the wallet, without the user leaving the sheet: document capture, the face check, agreements. It takes about two minutes, any national ID works from any supported country, and there is no SSN requirement. When a user hits it mid-flow, the wallet picks up where they left off as soon as they're approved. You see the outcome as a status, never the documents. Progress arrives as `identity.verification.updated`. ### 2. Balance goes in Balance is the money behind Agentcards. Users add it inside the wallet, or your company keeps a shared balance and funds users from it. That choice is your funding model, and it locks at your first live transaction, so pick it before you launch. Money in shows up as `wallet.funded` (or `user_wallet.funded` when you fund a user from the company balance). ### 3. The Agentcard is issued With identity verified and balance behind them, the user gets their Agentcard in the wallet. Each one arrives as `card.created`. If you want to issue cards from your own server instead, that's an API product: see the [API reference](/companies/api/reference). ## Paying When an agent pays, the wallet handles approval: limits are checked, merchant locks are enforced, and purchases that need a human get an approval prompt. You see `transaction.authorized` when money moves, and `approval.requested` when a human is being asked. ## What you build around it Two things. A webhook endpoint, so your product knows what happened in the wallet. And the [Purchase API](/purchase/purchase-api), when you want agents to buy things end to end. ## The whole loop, once Your server creates a wallet link. Your product opens the wallet with it. The user adds their card. Your webhook endpoint receives `connected_card.updated`. That's a complete integration, and it's the exact loop the [Quickstart](/get-started/quickstart) walks through in sandbox. Next: [Choose your platform](/platforms/choose-your-platform) # Webhooks Source: https://docs.agentcard.sh/wallet/webhooks How your server finds out what happened in the wallet: the envelope, the signature, and every event we send. Everything that happens in the wallet reaches your server as a webhook. The SDK callbacks are UI signals; webhooks are the record. Create endpoints in the [dashboard](https://app.agentcard.sh) under **Webhooks**, or with `agent-cards companies webhooks create`. ## The envelope Every event has the same shape: ```json theme={null} { "id": "evt_...", "type": "connected_card.updated", "created": "2026-08-11T18:30:00.000Z", "livemode": false, "data": { ... } } ``` Delivery is at least once, so the same event can arrive twice. Deduplicate on `id`. ## Verify the signature Each delivery carries an `AgentCard-Signature` header: ``` AgentCard-Signature: t=1754938200,v1=5257a869e7... ``` To verify: take `t`, concatenate it with a dot and the raw request body, compute HMAC-SHA256 with your endpoint's signing secret, and compare it to `v1`. Reject anything older than a few minutes to block replays. ```javascript Node theme={null} const [t, v1] = header.split(",").map((p) => p.split("=")[1]); const expected = crypto .createHmac("sha256", process.env.AGENTCARD_WEBHOOK_SECRET) .update(`${t}.${rawBody}`) .digest("hex"); const valid = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1)); ``` ```python Python theme={null} import hmac, hashlib, os t, v1 = (part.split("=", 1)[1] for part in header.split(",")) expected = hmac.new( os.environ["AGENTCARD_WEBHOOK_SECRET"].encode(), f"{t}.{raw_body}".encode(), hashlib.sha256, ).hexdigest() valid = hmac.compare_digest(expected, v1) ``` Read the signing secret in the dashboard, or with `companies webhooks reveal`. If it leaks, `companies webhooks roll-secret` rotates it. There is also a legacy `X-AgentCard-Signature: sha256=...` header signing the body alone. New integrations should use `AgentCard-Signature`. ## Delivery and retries Failed deliveries retry with backoff, and a sweeper picks up anything that got interrupted. Recent deliveries, with payloads and response codes, are visible in the dashboard and with `companies webhooks deliveries`. Send yourself a test event any time: ```bash theme={null} agent-cards companies webhooks test ``` ## Every event we send By default an endpoint receives everything. You can filter to specific types per endpoint. | Object | Events | | ------------ | ----------------------------------------------------------------------------------------------------------- | | Connections | `connection.created` | | Added cards | `connected_card.updated` | | Identity | `identity.verification.updated` | | Cards | `card.created` · `card.updated` · `card.closed` | | Cardholders | `cardholder.created` · `cardholder.updated` · `cardholder_onboarding_session.completed` | | Transactions | `transaction.authorized` · `transaction.declined` · `transaction.cleared` · `transaction.voided` | | Card flows | `card_flow.started` · `card_flow.failed` | | Approvals | `approval.requested` | | Balance | `wallet.funded` · `user_wallet.funded` · `wallet.balance.low` · `balance.low` | | Transfers | `transfer.initiated` · `transfer.approved` · `transfer.completed` · `transfer.failed` · `transfer.released` | | Recoveries | `recovery.requested` · `recovery.completed` · `recovery.rejected` | | Withdrawals | `wallet.withdrawal.initiated` · `wallet.withdrawal.completed` · `wallet.withdrawal.failed` | | Wallet links | `wallet_link.opened` | | Rewards | `reward.earned` · `reward.reversed` | | Merchants | `merchant.connected` | | Platform | `platform_connect_session.completed` | ## When it fails If your endpoint is down, deliveries retry; nothing is lost, but act on the dashboard's failure indicators. If signatures stop verifying, check that you're reading the raw body (not re-serialized JSON) and the right endpoint's secret. ## Sandbox behavior Sandbox events deliver exactly like production, with `livemode: false`. The quickstart's events are real deliveries you can inspect. Next: [Purchase API](/purchase/purchase-api)