Virtual Cards
Bitnob’s Virtual Cards API allows you to programmatically issue and manage USD-denominated virtual debit cards for users. These cards can be used for online purchases, subscriptions, and other payments just like physical debit cards.
Migrating from v1
If you integrated against the legacy card endpoints, a few paths have been consolidated in v2. The intent is one endpoint per resource transition, with the specific action carried in the body — simpler to build SDKs against and easier to observe in logs.
v2 consolidations (breaking changes from v1)
Fund + Withdraw → single POST /api/cards/:cardId/balance discriminated by type: "fund" | "withdraw" in the body.
Freeze + Unfreeze → single POST /api/cards/:cardId/status discriminated by status: "frozen" | "active" in the body.
Termination → DELETE /api/cards/:cardId with a reason in the body (was POST /api/cards/:cardId/terminate in v1).
Secure details → GET /api/cards/:cardId/secure (was /details in v1).
Spend limits → PUT /api/cards/:cardId/spend-limits (plural renamed from spending-limits) — covers transaction, daily, weekly, monthly, yearly, all_time limits.
All amount fields on card endpoints are in the base units of the card currency. On a USD card the base unit is cents — so 5000000 means $50,000.00 (5,000,000 ÷ 100), not five million dollars. Apply the same divisor on every amount field (funding, withdrawals, spend limits, balances).
Create Card
Issue a new virtual or physical card for a customer. The card is created in an active state and can be used immediately after funding.
Create Card Body Parameters
Initial funding amount for the card, in the currency's smallest unit (e.g. 5000000 = $50.00 when currency is USD).
The type of card to issue. Accepted values: 'virtual' or 'physical'.
Currency the card will transact in (e.g. 'USD'). All purchases settle in this currency.
Name to emboss / associate with the card (typically the cardholder's full name).
Whether contactless (NFC) payments are enabled on the card.
Identifier of the user or operator issuing the card — stored on the audit trail.
HTTPS URL that will receive lifecycle and transaction webhooks for this card.
Spend controls applied to the card. Each limit is a string-formatted decimal in the card currency.
Maximum amount per single transaction (e.g. '1000.00').
Maximum cumulative spend per day (e.g. '5000.00').
Maximum cumulative spend per 7-day rolling window (e.g. '20000.00').
Maximum cumulative spend per calendar month (e.g. '50000.00').
Cardholder identity and address. Required fields vary by ID type and country.
'individual' or 'business'.
Cardholder's given name.
Cardholder's family name.
Cardholder's email address.
Local phone number without the country code (e.g. '8034567890').
International dial code for the phone number (e.g. '+234').
Cardholder's date of birth in YYYY-MM-DD format.
Government ID type used for KYC (e.g. 'national_id', 'passport', 'drivers_license').
Value of the ID document referenced by id_type.
Street address line.
City of the cardholder's address.
State or province of the cardholder's address.
Postal / ZIP code.
Three-letter ISO 3166-1 alpha-3 country code (e.g. 'NGA', 'USA', 'GBR').
The customer object on this request is used to create a new customer record as part of card issuance you do not need to call the Customers API first. The customer returned on the response can be reused for subsequent cards or other endpoints.
Response
Top-level flag indicating whether the request was processed successfully.
Human-readable status message describing the outcome.
The created card record.
Unique identifier for the newly created card. Use it on subsequent lifecycle endpoints (fund, freeze, etc.).
Identifier of the company that owns this card.
Identifier of the customer the card was issued to (created implicitly from the request's customer object).
The type of card issued — 'virtual' or 'physical'.
Overall card status. A freshly created card is 'pending' until the issuer completes provisioning, then transitions to 'active'.
Fine-grained provisioning status. 'processing' while the issuer is minting the card; becomes 'completed' once the PAN is generated and masked_pan is populated.
Name associated with the card (as provided in the request).
Display name for the card. Defaults to name if no preferred name is supplied.
Masked card number (e.g. '**** **** **** 4532'). Empty string while created_status is 'processing'; populated once provisioning completes.
Current balance as a string in the card's smallest unit. Starts at '0' and updates on fund / withdraw operations.
Currency of the balance (e.g. 'USD').
Balance rendered as a decimal number for display (e.g. 0, 50.25).
Whether contactless (NFC) payments are enabled on the card.
Applied spending limits (single_transaction, daily, weekly, monthly). Empty object when no limits are in effect.
Server-generated reference for tracking the card creation (e.g. 'CARD_CREATE_49C9464924DC').
Webhook URL that will receive lifecycle and transaction events for this card.
Stringified JSON of metadata you attached (e.g. '{}' when empty).
Billing address printed on the card statement (line1, line2, city, state, postal_code, country). Returned as the issuer's billing address, which may differ from the cardholder's address.
Identifier of the user or operator that issued the card, echoed from the request.
RFC 3339 / ISO 8601 timestamp of when the card was created (UTC).
RFC 3339 / ISO 8601 timestamp of the last update to the card record (UTC).
Unique identifier for this API request, useful for log correlation and support.
Top-level RFC 3339 / ISO 8601 server timestamp of when the response was generated (UTC).
A freshly created card returns with status: "pending" and created_status: "processing". masked_pan is empty at this point. Listen for the card-ready webhook (or poll GET /api/cards/:cardId) to pick up the final status: "active" and the populated masked_pan.
Get Card
Retrieve the details of a specific card by its ID. This returns general card information including balance and status, but not sensitive card data like the full card number or CVV.
Path Parameters
The unique identifier of the card to retrieve.
Get Card Details (Sensitive)
Retrieve sensitive card data including the full card number, CVV, and expiry date. This endpoint returns PCI-sensitive information and should be used with extreme care.
Only call this endpoint when absolutely necessary. Never log or store the full card number or CVV. Display card details securely in the UI and consider implementing additional verification (e.g., 2FA) before showing details to end users.
Path Parameters
Top-level flag indicating whether the request was processed successfully.
Human-readable status message describing the outcome.
Envelope wrapping the sensitive card credentials.
Identifier of the card whose credentials are being returned.
Full 16-digit PAN. PCI-sensitive — do not log, store, or transmit outside a PCI-compliant surface. Render directly to the end-user's device and drop immediately after use.
3-digit card verification value. PCI-sensitive — apply the same handling rules as card_number.
Two-digit expiry month, zero-padded (e.g. '04').
Four-digit expiry year (e.g. '2029').
Name associated with the card (cardholder name).
Unique identifier for this API request, useful for log correlation and support.
Top-level RFC 3339 / ISO 8601 server timestamp of when the response was generated (UTC).
This response carries the unmasked PAN and CVV — treat the entire payload as PCI-regulated. Never log it, never persist it, and never forward it to non-compliant services. Render directly in an iframe or PCI-scoped surface and discard immediately.
List Cards
Retrieve a paginated list of cards belonging to your company. Filter by card type, status, or a search string (e.g. the last 4 digits of the PAN or the cardholder name).
Query Parameters
Filter by card type. Accepted values: 'virtual' or 'physical'.
Filter by card status (e.g. 'active', 'pending', 'frozen', 'terminated').
Free-text search across cards — matches the last 4 digits of the PAN or the cardholder name.
Opaque base64 pagination cursor. Pass data.page_info.next_cursor from the previous response to fetch the next page. Omit on the first request.
Maximum number of records to return per page. Default 20.
Card Transactions
Retrieve a paginated list of transactions on a single card — fees, funding, purchases, refunds. Pagination is cursor-based.
Query Parameters
Opaque base64 pagination cursor. Pass data.page_info.next_cursor from the previous response to fetch the next page, or data.page_info.previous_cursor to go back. Omit on the first request.
Maximum number of records to return per page. Default 20.
Filter by transaction type (e.g. 'fee', 'funding', 'purchase', 'refund', 'withdrawal').
Filter by transaction status (e.g. 'pending', 'completed', 'failed').
Response Fields
Page of card transaction records.
Unique identifier of the transaction.
Identifier of the card this transaction belongs to.
Identifier of the company that owns the card.
Transaction type: 'fee', 'funding', 'purchase', 'refund', 'withdrawal'.
Lifecycle status: 'pending', 'completed', or 'failed'.
Transaction amount as an integer string in the card's smallest unit (e.g. '5000000' = $50.00 on a USD card).
Currency of the transaction.
Transaction amount as a decimal number for display.
Card balance immediately before this transaction, in the smallest unit.
Card balance immediately after this transaction, in the smallest unit.
Fee charged for this transaction, in the smallest unit. Zero when no fee applies.
Category of the fee (e.g. 'CARD_CREATION_FEE', 'CARD_TOPUP_FEE'). Empty when fee_amount is zero.
Server-generated reference string for this transaction.
Human-readable description of the transaction (e.g. 'Card creation fee', 'Initial card funding').
Stringified JSON of metadata attached at transaction time.
Name on the card at the time of the transaction.
Last four digits of the card PAN at the time of the transaction. Present on transactions against a provisioned card.
Identifier returned by the downstream card processor (e.g. Miden). Use it when opening support tickets with the provider. Not present on purely internal transactions like creation fees.
Identifier of the user or operator that initiated the transaction.
RFC 3339 / ISO 8601 timestamp of when the transaction was recorded.
RFC 3339 / ISO 8601 timestamp of when the transaction record was created.
RFC 3339 / ISO 8601 timestamp of the most recent update to the record.
Machine-readable failure code. Only present when status is 'failed' (e.g. 'PROVIDER_REJECTED').
Human-readable explanation of the failure, often surfaced from the underlying card processor. Only present when status is 'failed'.
Cursor-based pagination metadata.
True when more records are available via next_cursor.
True when prior records are available via previous_cursor.
Opaque base64 cursor for fetching the next page.
Opaque base64 cursor for stepping back one page.
Total number of transactions for this card matching the current filters.
Get Card Transaction
Retrieve a single card transaction by its ID, scoped to a specific card. Use this to re-fetch an updated status after a webhook, or to confirm a specific provider_transaction_id / fee_amount before reconciling.
Path Parameters
The card the transaction belongs to.
The transaction to retrieve (from data.transactions[].id on the listing endpoint).
The returned data.transaction object uses the identical schema as entries in the Card Transactions list response — refer to that section for per-field docs, including the provider_transaction_id, failure_code, and failure_reason fields that may or may not be present depending on transaction type and status.
Fund or Withdraw
A single endpoint adjusts a card's balance. The type field in the body selects the direction "fund" credits the card, "withdraw" debits it. Both operations are processed asynchronously and resolve via the card transactions webhook.
Path Parameters
The card to adjust. Must belong to your company.
Body Parameters
Amount to move in the card currency's smallest unit (e.g. 2000000 = $20.00 on a USD card).
Direction of the balance change: 'fund' to credit the card, 'withdraw' to debit it back to your account.
Client-generated identifier for this operation. Used for idempotency — retrying with the same reference returns the original transaction instead of creating a duplicate.
The response returns immediately with data.transaction.status: "pending" and balance_before === balance_after. The card's balance updates once the provider confirms the operation subscribe to the card transactions webhook or poll GET /api/cards/:cardId/transactions for the final status.
Response
Top-level flag indicating whether the request was accepted.
Human-readable status of the request.
Provider-level acceptance flag. Returns false while the provider is still processing — watch the webhook for the final terminal state.
Provider-level status message (e.g. 'Card funding processing').
The card transaction record created for this operation. Identical shape to entries in the Card Transactions endpoint — see that section for per-field explanations.
'funding' for a fund request; 'withdrawal' for a withdraw request.
Lifecycle status. Starts as 'pending' on creation and transitions to 'completed' or 'failed' once the provider settles.
Card balance (smallest unit) before the operation. Equal to balance_after while the transaction is pending.
Card balance (smallest unit) after the operation completes. Updates once the provider confirms.
Fee charged for this operation, in the smallest unit. Funding and withdrawal both incur a 'funding_fee' fee_type.
Request identifier for log correlation and support.
RFC 3339 / ISO 8601 server timestamp of when the response was generated (UTC).
Update Card Status
A single endpoint flips a card between active and frozen, the direction is carried in the body as status: "frozen" | "active".
Freezing blocks all new authorizations while preserving the balance and PAN; unfreezing restores the card to full use. Use this for incident response (lost/stolen), temporary suspensions, or re-enabling after review.
Path Parameters
The unique identifier of the card to freeze or unfreeze.
Body Parameters
Target status: 'frozen' to block new authorizations, 'active' to restore. Other values are rejected.
Response Fields
Top-level flag indicating the request was accepted.
Human-readable outcome — 'Card frozen successfully' or 'Card activated successfully'.
Provider-level acceptance flag.
Provider-level status message.
Slim card projection after the status change — includes identity, current status, masked PAN, balance, card brand, cardholder name, reference, and display amount. Not the full card record; call Get Card by ID for everything else.
New card status: 'frozen' or 'active'.
Unique identifier for this API request, useful for log correlation.
Top-level RFC 3339 / ISO 8601 server timestamp (UTC).
Terminate Card
Permanently terminate a card. This action is irreversible — any remaining balance is returned to your company wallet.
You cannot terminate a card within 24 hours of creation. The API will reject the request until the card has been active for at least 24 hours — freeze the card instead (POST /api/cards/:cardId/status with status: "frozen") if you need to block use immediately, then come back to terminate once the cooling period has passed.
Path Parameters
The unique identifier of the card to terminate.
Body Parameters
Reason for terminating the card. Stored for audit and compliance (e.g. 'Employee departure', 'Card compromised', 'End of contract').
Response
Identifier of the card that was terminated.
Updated card status. 'terminated' after this operation.
The balance that was on the card at the time of termination, in the currency's base units.
True if the remaining balance was returned to your company wallet.
RFC 3339 / ISO 8601 timestamp of when the card was terminated (UTC).
The reason provided on the request, echoed back.
Terminated cards remain visible on GET /api/cards (with status: "terminated") and on GET /api/cards/:cardId so you can reconcile the final ledger. They cannot be funded, unfrozen, or reused — issue a new card if the cardholder needs continued spending.
Update Spending Limits
Update the spending limits and merchant restrictions on a card. All fields are optional — only the fields you include will be updated.
Path Parameters
The unique identifier of the card whose spending limits are being updated.
Body Parameters
Maximum amount allowed per individual transaction as a decimal string.
Maximum total spending allowed per day as a decimal string.
Maximum total spending allowed per week as a decimal string.
Maximum total spending allowed per month as a decimal string.
List of allowed MCC (Merchant Category Code) categories. Transactions at merchants outside these categories will be declined.
List of blocked MCC categories. Transactions at merchants in these categories will be declined.
Whitelist of specific merchant IDs. When set, only transactions at these merchants are allowed.
Blacklist of specific merchant IDs. Transactions at these merchants will be declined.
Response
Identifier of the card whose spending limits were updated.
The operation type. Value is update_spending_limits for this endpoint.
The full set of updated spending limits, including single_transaction, daily, weekly, monthly, allowed_categories, blocked_categories, allowed_merchants, and blocked_merchants.
ISO 8601 timestamp of when the spending limits were last updated.
Get Cards by Customer
Retrieve all cards belonging to a specific customer.
Path Parameters
Indicates whether the API request was successful or failed.
A human-readable message describing the result of the request.
A list of cards associated with the specified customer.
The unique identifier of the card.
The unique identifier of the customer who owns the card.
The type of card issued to the customer (e.g., virtual or physical).
The card network brand associated with the card (e.g., Visa or Mastercard).
The last four digits of the card number used for identification.
The currency the card operates in.
The current status of the card (e.g., active, inactive, blocked).
The current available balance on the card.
The date and time when the card was created, returned in ISO 8601 format.
The total number of cards returned in the response.