Issue a Virtual Card
Virtual cards are issued instantly and are immediately active — no activation step, no physical fulfillment. This guide covers issuance, the two retrieval modes (non-sensitive and sensitive), and how to handle the card securely.
Prerequisites
- The cardholder must have
status = 1(Approved). See Onboard a Cardholder End to End. - You have program-level credentials and a valid mTLS certificate.
Attempting to issue a card to a Cardholder with any status other than
1(Approved) will fail. Always checkstatusbefore calling the issuance endpoint if there's any possibility the cardholder hasn't been through compliance yet.
Step 1: Issue the virtual card
POST /cards/virtual/{cardholderId}
Request fields
| Field | Required | Type | Constraints | Notes |
|---|---|---|---|---|
nameOnCard | Yes | string | 1–26 chars | The name that will appear on the virtual card. Defaults to the cardholder's cardHolderFirstName + cardHolderLastName if not specified — best practice is to pass it explicitly |
alias | No | string | — | A localized display alias to distinguish between a cardholder's multiple cards (e.g., "Personal Spending", "Travel Card") — visible to your application, not the card network |
transactionLimit | No | integer | Decimal-implied; cannot exceed the cardholder's transactionLimit | Card-level spending ceiling. If omitted, inherits the cardholder's limit (which itself inherits the Program's ceiling if unset) |
callingCode | No | string | 1–3 digits, zero-padded to 3 | Card-specific mobile dialing code — defaults to cardholder's if not set |
countryCallingCode | No | string | Alpha-2 | Card-specific country code for phoneNum |
phoneNum | No | string | 10–20 digits | Card-specific landline — defaults to cardholder's if not set |
cellNum | No | string | 10–20 digits | Card-specific mobile — defaults to cardholder's if not set |
emailAdr | No | string | 6–50 chars | Card-specific email — defaults to cardholder's if not set |
Example request
{
"nameOnCard": "JORDAN REYES",
"alias": "Personal Spending",
"transactionLimit": 50000
}transactionLimit: 50000 = $500.00 in a USD-denominated Program.
Response
{
"status": "success",
"cardId": 8821,
"type": "virtual",
"maskedCardNumber": "************1234",
"cryptoAddresses": [
{ "chain": "evm", "address": "0x742d35Cc6634C0532925a3b8D4C9B7e..." },
{ "chain": "bitcoin", "address": "bc1qxy2kgdygjrsqtzq2n0yrf249..." },
{ "chain": "solana", "address": "7EqQdEULxntMjTnxHhq1p9RwP..." }
]
}Save cardId — it's the key for all subsequent operations on this card. Note that:
- The card is immediately
issuerCardStatus = 1(Active) — no activation required. cryptoAddressesare provisioned at this moment and ready to receive digital-asset deposits.maskedCardNumbershows only the last 4 digits — to display full card details, see Step 2.
Step 2: Retrieve card details
There are two retrieval modes with different sensitivity profiles:
Non-sensitive retrieval (safe for server-side storage and display)
GET /cards/{cardId}
Returns CardViewMasked:
| Field | Description |
|---|---|
cardId | Card ID |
cardholderId | Owning cardholder |
cardStatus | Approval status (0 Pending · 1 Approved · 2 Declined) |
issuerCardStatus | Operational status (0 Pending · 1 Active · 2 On Hold · 3 Closed · 4 Not Activated) |
maskedCardNumber | PAN masked to last 4 digits |
type | virtual or physical |
alias | The card's display alias |
transactionLimit | Card-level limit (decimal-implied) |
cryptoAddresses | Full deposit address array |
contact | Card-level contact details (defaults to cardholder's if not set at issuance) |
This response is safe to log, store, and display in administrative UIs.
Sensitive retrieval (full PAN, expiry, CVV)
GET /cards/{cardId}/details
This endpoint returns the full, unmasked PAN and CVV in plaintext, despite the name of its underlying schema (
CardDetailsMasked). Treat the response as the most sensitive data this API returns:
- Call it only when displaying full card details directly to the cardholder (e.g., to allow manual entry into a merchant form or digital wallet) — and only over a channel meeting PCI-DSS requirements for cardholder data display.
- Never log the response.
- Do not persist
cardNumberorcvvin your systems beyond the immediate display use case.- If you're building a "show my card details" UX, render the values in the client UI and discard them server-side immediately after passing them to the client — do not cache.
Returns CardDetailsMasked (the schema name notwithstanding — the actual content is fully unmasked):
| Field | Description |
|---|---|
cardNumber | Full 16-digit PAN (unmasked) |
expMonth | Expiry month (MM) |
expYear | Expiry year |
cvv | CVV/CVC code |
cryptoAddresses | Deposit address array |
Step 3: Check status and balance
GET /cards/{cardId}/status → { "issuerCardStatus": 1 }
GET /cards/{cardId}/balance → { "ledgerBalance": 0, "availableBalance": 0, "currency": 0 }
A newly issued virtual card has zero balance. To fund it, see Fund a Card Automatically via Crypto or Fund a Card via Bank Transfer.
Issuing multiple cards to one cardholder
A single approved Cardholder can hold multiple virtual cards simultaneously. Each card has its own:
cardIdandmaskedCardNumberissuerCardStatus(managed independently)transactionLimit(subject to the same inheritance ceiling)cryptoAddresses(unique per card)alias(useful for distinguishing cards in your UI)
Use GET /cards?cardholderId={cardholderId} to list all cards for a given cardholder.
