Issue and manage cards

This recipe takes an individual customer from creation through card issuance, funding, and withdrawal.

Before you start, identify the funded internal account that will back and fund the card. Every POST request in this recipe requires an Idempotency-Key. Use a new value for each operation, and reuse that value only when retrying the same operation with the same payload.

Flow overview

sequenceDiagram
    autonumber
    actor Client
    participant API as Gravv API
    actor Customer

    Client->>API: Create customer and start verification
    API-->>Customer: Verification session
    Customer->>API: Complete verification
    Client->>API: Create backing account and activate cards
    alt Liveness or manual review required
        API-->>Client: pending_liveness or manual_review
        Client->>API: Poll application status
    else Application approved
        API-->>Client: approved
    end
    Client->>API: Issue card
    loop Until card is ready
        Client->>API: Retrieve card
        API-->>Client: Current card status
    end
    Client->>API: Fund or withdraw from card
    API-->>Client: Processing result

Create the individual customer who will own the card.

curl --request POST \
--url https://api.gravv.xyz/v1/customers \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: customer-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"first_name": "Sarah",
"last_name": "Johnson",
"email": "sarah.johnson@example.com",
"phone": "+14155552345",
"type": "individual",
"gender": "female",
"date_of_birth": "1990-03-15",
"address": {
"address_line1": "123 Market Street",
"city": "San Francisco",
"postal_code": "94102",
"state": "US-CA",
"country": "US"
}
}'
{
"data": {
"id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"first_name": "Sarah",
"last_name": "Johnson",
"type": "individual",
"status": "active"
},
"error": null
}

Save data.id as the customer ID.

Start KYC for the customer, then direct them to the returned verification URL. Wait for KYC to reach completed before continuing.

curl --request POST \
--url https://api.gravv.xyz/v1/customers/kyc/start \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: kyc-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310"
}'
{
"data": {
"status": "success",
"web_url": "https://verify.gravv.xyz/websdk/p/example_token"
},
"error": null
}

Use KYC status or customer KYC webhooks to confirm completion.

Create the USD account that will back the customer's card.

curl --request POST \
--url https://api.gravv.xyz/v1/accounts \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: card-account-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"currency": "USD",
"type": "regular",
"blockchain_network": "polygon",
"label": "Sarah Johnson card account",
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310"
}'
{
"data": {
"id": "5d9e677f-f071-4881-a861-7cdcebacd9d5",
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"currency": "USD",
"type": "regular",
"status": "active"
},
"error": null
}

Save data.id as the backing account ID.

Activate virtual_cards. This creates the card application used for issuance.

curl --request POST \
--url https://api.gravv.xyz/v1/risk/features/activate \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: card-activation-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"feature_id": "virtual_cards",
"provider_data": {
"annual_remuneration": 75000,
"estimated_monthly_limit": 2000,
"ip_address": "192.0.2.10",
"account_id": "5d9e677f-f071-4881-a861-7cdcebacd9d5"
}
}'
{
"data": {
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"feature_id": "virtual_cards",
"status": "active",
"activated_at": "2026-01-09T10:29:16.000Z",
"data": {
"application_id": "a14ec356-f33d-4384-ba68-4d9bfa19765b"
}
},
"error": null
}

Save data.data.application_id. If activation returns pending_liveness, send the customer to websdk_url, complete the check, and retry with the same idempotency key.

Retrieve the application until application_status becomes approved. Stop if it becomes denied, locked, or canceled.

curl --request GET \
--url https://api.gravv.xyz/v1/cards/applications/a14ec356-f33d-4384-ba68-4d9bfa19765b \
--header 'Api-Key: <API_KEY>'
{
"data": {
"id": "a14ec356-f33d-4384-ba68-4d9bfa19765b",
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"application_status": "approved"
},
"error": null
}

Create the card after the application is approved.

curl --request POST \
--url https://api.gravv.xyz/v1/cards \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: card-issuance-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"card_type": "virtual",
"card_limit": 500,
"name_on_card": "Sarah Johnson"
}'
{
"data": {
"id": "dce0192a-9b3d-440b-9500-33dc9ac8dc20",
"message": "Card creation request is processing",
"status": true,
"fee_charged": true,
"fee_pending": false
},
"error": null
}

Save data.id as the card ID. Retrieve the card until its status is ready for use.

Transfer funds from a funded internal account to the issued card.

curl --request POST \
--url https://api.gravv.xyz/v1/transfer \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: card-funding-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"source": {
"source_type": "internal_account",
"id": "b9c2cb0d-e09a-496f-8655-d8c4b7aaf2f8"
},
"destination": {
"destination_type": "card",
"id": "dce0192a-9b3d-440b-9500-33dc9ac8dc20"
},
"amount": 250,
"description": "Initial card funding",
"customer_id": "9e3cccad-e9ae-47a0-81ee-063af0159310",
"client_reference": "card-fund-2026-001"
}'
{
"data": {
"amount": "250",
"reference": "550e8400-e29b-41d4-a716-446655440000",
"transfer_status": "pending",
"transaction_id": "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
},
"error": null
}

Track the transfer until it completes before allowing the customer to spend the funds.

Move unused card funds back to the account configured during card activation.

curl --request POST \
--url https://api.gravv.xyz/v1/cards/withdraw \
--header 'Api-Key: <API_KEY>' \
--header 'Idempotency-Key: card-withdrawal-<UUID>' \
--header 'Content-Type: application/json' \
--data '{
"card_id": "dce0192a-9b3d-440b-9500-33dc9ac8dc20",
"amount": 100
}'
{
"data": {
"tx_hash": "0x2e5d0af9189b4b4c23bc64b96295e730609ec91e50e452a5da0a4af4bfe5c192"
},
"error": null
}

See Cards, card applications, and the Cards API Reference for lifecycle and status details.

Was this page helpful?