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.
Type: boolean · Required: No
Enable contactless (NFC/tap) payments on the card.
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.
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.
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.
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.