---
title: "Cards"
description: "Choose the recipe for issuing USD Visa cards: verify the cardholder, open the card application, issue, fund, limit, freeze, show details and close."
---
Use these recipes to issue virtual USD Visa cards to your customers and to manage the cards after issuance. You verify cardholders through the `/v1/risk/` routes, which serve cards only.

**Money example.** You issue Ada a virtual card with a 300 USD daily limit. Its balance is 0. You move 150 USD from her account to the card, and she pays 18.75 USD for a streaming subscription. The card now holds 131.25 USD.

## Two words used everywhere

- A **card account** is the one account of type `card` that your business opens once. It backs every card you issue, and its id is the `account_id` you send at every activation. See [Set up your card account](/platform/cards/set-up-your-card-account).
- A **card group** is the balance a card spends from. Cards can share one balance (`funding.mode` `shared`) or each have their own (`own`). A balance belongs to one cardholder.

## Choose your recipe

| You want to | Status | Recipe |
| :-- | :-- | :-- |
| Give a person a virtual card after a hosted identity check | Available | [Issue a virtual card after a hosted check](/recipes/cards/issue-a-virtual-card-after-a-hosted-check) |
| Verify a cardholder with ID photos from your own app | Available | [Verify a cardholder with your own ID capture](/recipes/cards/verify-a-cardholder-with-your-own-id-capture) |
| Give each employee of a company a card with its own balance | Available | [Issue expense cards to employees](/recipes/cards/issue-expense-cards-to-employees) |
| Print the company's name on an employee's card | Sandbox only; Not yet available in live | [Issue a business card](/recipes/cards/issue-a-business-card) |
| Put money on a card | Available | [Top up a card](/recipes/cards/top-up-a-card) |
| Change how much a card can spend per day | Available | [Change a card's daily limit](/recipes/cards/change-a-cards-daily-limit) |
| Freeze, unfreeze or block a lost card, and replace it | Available | [Freeze or block a lost card](/recipes/cards/freeze-or-block-a-lost-card) |
| Find out why a payment was declined | Decline reasons through the API: Not yet available | [Find out why a payment was declined](/recipes/cards/find-out-why-a-payment-was-declined) |
| Show the card number, CVV and PIN, and change the PIN | Available | [Show card details and set a PIN](/recipes/cards/show-card-details-and-set-a-pin) |
| Close a card and get its money back | Not yet available for new cards | [Close a card and get the money back](/recipes/cards/close-a-card-and-get-the-money-back) |
| Give the same person a second card, or a physical card | Virtual: Available. Physical: Not yet available | [Issue a second or physical card](/recipes/cards/issue-a-second-or-physical-card) |

Cards are USD only. Any other card currency is Not supported.

## Sandbox and live differ

| Step | Sandbox | Live |
| :-- | :-- | :-- |
| Card application | Approved at once | Reviewed. Wait for `cards.application.update` with `approved` |
| First card | Issued during activation, with a daily limit equal to `estimated_monthly_limit` | Issue it with `POST /v1/cards` once approved |
| Physical cards | Issuance fails with `card creation failed!` | No activation route. Not yet available |

## Rules every recipe relies on

- Send every request to one host, `https://api.gravv.xyz`, with the `Api-Key` header. A key that starts with `grvSec_sandbox_` reaches sandbox; any other key is live.
- Every POST needs its own new `Idempotency-Key`. Gravv replays a 4xx for 24 hours under its key, so use a new key after you fix a request.
- When you activate cards, always send `provider_data.account_id`, which is your card account.
- **Always send `status` and `limit` together** on `PATCH /v1/cards/{card_id}/update`. An update without `limit` saves the daily limit as 0.
- Never call `POST /v1/risk/start-kyc` for a customer whose `basic` KYC is already `completed`. The call resets it to `pending`.
- Gravv sends no webhook when it issues a card. Read the card with `GET /v1/cards`.
- Every card error is a 400, including not found. Read the `error` field for the reason.
- Deduplicate webhooks on the body's `event_id`.
