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

# Verify a Card

> Authorize a saved card for agentic use with a one-time device-binding ceremony.

## Introduction

A saved card is not automatically usable by agents. Before agents can use it to pay, the card must be **verified for agentic payments**. Verification is a one-time step per card that produces two things:

1. **Account verification** via a one-time code sent to the user's email.
2. **A passkey bound to the user's device**, so future payment authorizations only need a single passkey tap.

Once a card is verified, every subsequent card permission can be authorized by the user with their passkey alone. The email step is not repeated.

## Prerequisites

* A saved card. See [Save a Card](/agents/payment-methods/cards/save-card) if you have not done this yet.
* The `paymentMethodId` of the saved card.
* An authenticated user with a JWT.

<Note>
  **Eligible Cards**

  You can currently use card permissions with Mastercard and eligible U.S.-issued Visa credit and debit cards.

  **Not supported for Visa:** non-US cards, business cards, prepaid cards, Chase cards, Fidelity cards.

  For AMEX and Ramp cards, [contact us](https://www.crossmint.com/contact).
</Note>

## Steps

<Steps>
  <Step title="Check the card's current verification status">
    Before starting verification, check whether the card is already verified. The API returns a `status` that is always one of the following:

    | Status        | Meaning                                                                                               |
    | :------------ | :---------------------------------------------------------------------------------------------------- |
    | `not_started` | Verification has not been started yet.                                                                |
    | `pending`     | Verification was started; the user still needs to complete the challenge (email code and/or passkey). |
    | `active`      | Verification finished; the card is ready for agentic card permissions.                                |

    ```typescript theme={null}
    const BASE_URL = "https://staging.crossmint.com/api/unstable";

    async function checkEnrollment(jwt: string, paymentMethodId: string) {
        const response = await fetch(
            `${BASE_URL}/payment-methods/${paymentMethodId}/agentic-enrollment`,
            {
                headers: {
                    "Content-Type": "application/json",
                    "X-API-KEY": CROSSMINT_CLIENT_API_KEY,
                    Authorization: `Bearer ${jwt}`,
                },
            }
        );

        return response.json();
        // { status: "active" | "pending" | "not_started", ... }
    }
    ```

    If the status is `active`, verification is complete and you can skip directly to [Give Card Permission](/agents/payment-methods/cards/create-card-permissions). If it is `pending`, reuse the existing verification response in the next step rather than creating a new one.
  </Step>

  <Step title="Start verification">
    If the card is `not_started`, send a `POST` request with the user's email to begin verification. The response includes an `enrollmentId` and a `verificationConfig` that the verification component uses to run the passkey ceremony.

    ```typescript theme={null}
    async function startEnrollment(
        jwt: string,
        paymentMethodId: string,
        email: string
    ) {
        const response = await fetch(
            `${BASE_URL}/payment-methods/${paymentMethodId}/agentic-enrollment`,
            {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "X-API-KEY": CROSSMINT_CLIENT_API_KEY,
                    Authorization: `Bearer ${jwt}`,
                },
                body: JSON.stringify({ email }),
            }
        );

        return response.json();
    }
    ```

    Example response:

    ```json theme={null}
    {
        "enrollmentId": "enr_abc123",
        "status": "pending",
        "verificationConfig": {
            "environment": "test",
            "publicApiKey": "YOUR_PUBLIC_API_KEY"
        }
    }
    ```
  </Step>

  <Step title="Render the verification component">
    Pass the pending verification response to the `PaymentMethodAgenticEnrollmentVerification` component. It runs the full device-binding ceremony inside your UI — the user receives an email code, enters it, and then creates a passkey bound to the current device.

    ```tsx theme={null}
    import { PaymentMethodAgenticEnrollmentVerification } from "@crossmint/client-sdk-react-ui";

    function EnrollCardStep({ enrollment }: { enrollment: PendingEnrollment }) {
        return (
            <PaymentMethodAgenticEnrollmentVerification
                enrollment={enrollment}
                onVerificationComplete={() => {
                    console.log("Card is now verified for agentic use");
                }}
                onVerificationError={(error) => {
                    console.error("Verification failed", error);
                }}
            />
        );
    }
    ```

    When `onVerificationComplete` fires, the server has flipped the verification to `active`. The card is now ready for agentic card permissions.
  </Step>
</Steps>

## Understanding the Verification Flow

The `PaymentMethodAgenticEnrollmentVerification` component runs the following sequence in order:

1. The server sends a one-time code to the user's email.
2. The user enters the code in the component's UI.
3. The component prompts the browser to create a passkey bound to this device.
4. The server marks the verification `active`.

**The email code and the passkey are not two separate checks.** The email code proves the user owns the account; the passkey then binds the device so that future payment authorizations can skip the email step. Both are part of a single device-binding ceremony.

## Common Gotchas

<AccordionGroup>
  <Accordion title="The email step only happens during verification">
    Once a card is verified, all subsequent payment authorizations use the passkey alone. If you see the email code prompt on a non-verification flow, the device is not yet bound — complete verification first.
  </Accordion>

  <Accordion title="A passkey is bound to a single device and browser profile">
    If the user clears browser state, switches browsers, or moves to a new device, they may be asked to re-verify for future actions on that new device.
  </Accordion>

  <Accordion title="Verification is per payment method, not per agent">
    Once a card is verified, any agent on the account can be given card permissions from it.
  </Accordion>
</AccordionGroup>

## What Is Next

With the card verified, the last step in this setup path is to [Give Card Permission](/agents/payment-methods/cards/create-card-permissions) so your agent can pay within scoped spending rules against the verified payment method.
