Virtual Card
Create and manage your Virtual Card.
Virtual Card
Create and manage your Virtual Card.
The Virtual Card API enables businesses and eligible personal accounts — including Personal Use — to apply for a USD Virtual Card, monitor application status, read issued-card balance/details/transactions, fund the card from Wallet or Bank, unload to Wallet, and use the card as a payment funding method. The product root mirrors Virtual Bank Account patterns under /veem/v1.2/virtual-card.
Prerequisites
Before submitting a Virtual Card application, ensure the following conditions are met:
- The referred customer account is eligible for Virtual Card (supported countries and account types). Business and Personal accounts are supported, including Personal Use. See Virtual Card Supported Countries.
- Business Ownership Information must be attested when required for the account type
- Required verification documents should be uploaded through the existing account document APIs (same document model used by Virtual Bank Account). Partners can submit an application before all documents are accepted; missing or rejected documents continue to appear in
validationErrors. Documents that are uploaded and pending Veem review do not appear as missing.
Veem performs additional validations before enabling a Virtual Card.
Authorization
OAuth2 token is required in the Authorization header.
HeadersThree headers are required to use this endpoint.
Name Type Required Description Authorizationstring Yes Bearer token (OAuth 2.0) This header holds the bearer token required to use Veem's public API, and belongs to the owner account. Failure to use a valid bearer token will result in a 404 (Not Found) status code in the response. An example of a valid header value is as follows: Bearer c047594b-082c-4da1-be89-08fe3770f4b3.
See Get Access Token section to generate your Bearer token.X-Request-Idstring Yes This is a unique string that identifies the current API request, and should follow the UUID format. Reusing this header without changing the value will result in a 409 (Conflict) status code in the response. An example X-request-id is as follows: 48855846-628d-4177-b071-80332a116f0aContent-Typestring Yes The content type of the request. Must be application/json
Virtual Card Endpoints
Veem's API provides a number of endpoints that can be used to configure Virtual Card scenarios.
Use the following base URL to access the endpoints in sandbox environment.
POST https://sandbox-api.veem.com/veem/Product root:
/veem/v1.2/virtual-card| Method | Path | Purpose |
|---|---|---|
POST | /v1.2/virtual-card | Submit Virtual Card application |
PATCH | /v1.2/virtual-card | Update an Initiated application draft |
GET | /v1.2/virtual-card | Get application status and issued cards |
GET | /v1.2/virtual-card/balance | List balances for all issued cards |
GET | /v1.2/virtual-card/balance/{fundingMethodId} | Get balance for one card |
GET | /v1.2/virtual-card/details | List details for all issued cards |
GET | /v1.2/virtual-card/details/{fundingMethodId} | Get details for one card |
GET | /v1.2/virtual-card/transactions/{fundingMethodId} | Get card transactions |
Money-moving writes reuse existing Payment and Invoice routes (no separate /virtual-card/deposit product route):
| Method | Path | Purpose |
|---|---|---|
POST | /v1.2/payments/wallet/withdraw | Fund card from Wallet |
POST | /v1.2/payments/withdraw | Fund card from Bank, or unload card to Wallet |
POST | /v1.2/payments / /v1.2/claimless-payments / /v1.2/invoices/claim | Pay from Virtual Card |
POST | /v1.2/invoices | Create invoice settling to Virtual Card |
Submit Virtual Card Application
Submits a Virtual Card application for a business or eligible personal account.
POST /v1.2/virtual-card
Partner Notes:
- A partner account can be configured to make this API request on behalf of their customers. This requires internal configuration for the partner account by Veem. Once the internal configuration is enabled, the API request can be made using partner account bearer token.
onBehalfAccountIdis an optional parameter in the request body when a partner makes an API request on behalf of their customer. This is the customer Veem account ID.- Successful submit creates or advances a Virtual Card regulatory application. It does not mean a Virtual Card funding method exists yet.
- Partners do not approve applications. Observe progress with
GET /v1.2/virtual-card.
Submit Virtual Card Application (Business)
{
"onBehalfAccountId": 123456,
"acceptTermsAndConditions": true,
"virtualCardBusinessInfo": {
"geographySendingCountries": "US",
"geographyReceivingCountries": "US",
"purposeOfAccount": "Business Virtual Card for providers",
"monthlyExpectedActivity": 9999999.99,
"annualRevenue": 9999999.99,
"businessRegistrationNumber": "123",
"businessIncorporationDate": "2010-01-01",
"sourceOfFunds": "It Services",
"businessWebsite": "https://mybusiness.com"
}
}{
"status": "Pending",
"virtualCardBusinessInfo": {
"geographySendingCountries": "US",
"geographyReceivingCountries": "US",
"purposeOfAccount": "Business Virtual Card for providers",
"monthlyExpectedActivity": 9999999.99,
"annualRevenue": 9999999.99,
"businessRegistrationNumber": "123",
"businessIncorporationDate": "2010-01-01",
"sourceOfFunds": "It Services",
"businessWebsite": "https://mybusiness.com"
},
"validationErrors": []
}Request Parameters
| Parameter | Mandatory | Format/Values | Size/Type |
|---|---|---|---|
onBehalfAccountId | No | Referred account ID where the Virtual Card application is submitted | Long |
acceptTermsAndConditions | Yes | Must be true on submit | boolean |
virtualCardBusinessInfo | Yes for business | Business regulatory information. Field meanings match the Virtual Bank Account business info shape; the wrapper name is virtualCardBusinessInfo. | object |
.geographySendingCountries | Yes | ISO 3166-1 alpha-2, comma-separated | string |
.geographyReceivingCountries | Yes | ISO 3166-1 alpha-2, comma-separated | string |
.purposeOfAccount | Yes | Purpose of the Virtual Card | string |
.monthlyExpectedActivity | Yes | Expected monthly activity amount | BigDecimal |
.annualRevenue | Yes | Annual revenue | BigDecimal |
.businessRegistrationNumber | Yes | Business registration number | string |
.businessIncorporationDate | Yes | YYYY-MM-DD | date |
.sourceOfFunds | Yes | Source of funds | string |
.businessWebsite | Conditional | Business website URL when required | string |
Submit Virtual Card Application (Individual / Sole Prop)
{
"onBehalfAccountId": 123456,
"acceptTermsAndConditions": true,
"virtualCardIndividualInfo": {
"geographySendingCountries": "PH",
"geographyReceivingCountries": "US",
"purposeOfAccount": "Contractor Virtual Card",
"monthlyExpectedActivity": 5000.00,
"sourceOfFunds": "Contracting income",
"occupation": "Software Engineer"
}
}{
"status": "Initiated",
"virtualCardIndividualInfo": {
"geographySendingCountries": "PH",
"geographyReceivingCountries": "US",
"purposeOfAccount": "Contractor Virtual Card",
"monthlyExpectedActivity": 5000.00,
"sourceOfFunds": "Contracting income",
"occupation": "Software Engineer"
},
"validationErrors": [
{
"field": "documents.PhotoIdFront",
"code": "REQUIRED",
"message": "Photo ID front is required"
}
]
}Field requirements differ by account type. Use submit and status responses (validationErrors) as the source of truth for remaining gaps.
Update Virtual Card Application Information
Updates an Initiated Virtual Card application draft. This does not re-run full validation advancement the way submit does; use it to correct stored regulatory fields.
PATCH /v1.2/virtual-card{
"onBehalfAccountId": 123456,
"virtualCardBusinessInfo": {
"businessWebsite": "https://updated-business.com",
"monthlyExpectedActivity": 25000.00
}
}
Partner Notes:
- Rejected applications are not repaired through PATCH. Start again with a new
POST /v1.2/virtual-card.X-Request-Ididempotency applies to POST and PATCH the same way as other partner APIs.
Get Virtual Card Application Status
Returns the partner-facing lifecycle view for the Virtual Card application, including issued-card identifiers after completion.
GET /v1.2/virtual-card{
"status": "Completed",
"virtualCardBusinessInfo": {
"geographySendingCountries": "US",
"geographyReceivingCountries": "US",
"purposeOfAccount": "Business Virtual Card for providers",
"monthlyExpectedActivity": 9999999.99,
"annualRevenue": 9999999.99,
"businessRegistrationNumber": "123",
"businessIncorporationDate": "2010-01-01",
"sourceOfFunds": "It Services",
"businessWebsite": "https://mybusiness.com"
},
"validationErrors": [],
"virtualCards": [
{
"accountId": 123456,
"fundingMethodId": 55182,
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"accountName": "Business Virtual Card",
"status": "Active",
"currency": "USD",
"timeCreated": 1761682203650
}
]
}Application statuses
| Status | Meaning |
|---|---|
Initiated | Application draft exists; may include validationErrors |
Pending | Pending Veem manual review / approval |
Approved | Internally approved; card provisioning may still be in progress |
Completed | Virtual Card funding method exists; virtualCards[] is populated |
Rejected | Rejected by Veem review or KYC; submit a new application |
Issued card discovery
There is no separate public card-list endpoint. Partners discover issued cards from virtualCards[] on this response.
Use fundingMethodId as the partner card identifier for balance, details, transactions, deposit, withdrawal, payer, and payee invoice operations. Do not use external card tokens, PAN, or other card-network identifiers.
Partner Notes:
onBehalfAccountIdis an optional query string parameter for partner-on-behalf reads.- Responses never include PAN, CVV, or other sensitive card credentials.
Validation Errors
validationErrors may appear on GET/POST/PATCH application responses.
{
"field": "documents.Address",
"code": "REQUIRED",
"message": "Proof of address is required"
}Document readiness rules for partners:
| Document state | Appears in validationErrors? |
|---|---|
| Never uploaded | Yes — missing document |
| Uploaded and pending Veem review | No |
| Rejected | Yes — missing document with rejection-specific wording |
| Accepted | No |
Internal card submission still blocks until required documents are accepted. Partner APIs intentionally hide pending-review documents from validationErrors so partners are not asked to re-upload documents that are already in review.
Get Virtual Card Balance
Balance endpoint provides the current spendable balance on the Virtual Card. Check balance before sending payments to avoid insufficient-funds failures.
GET /v1.2/virtual-card/balance
GET /v1.2/virtual-card/balance/{fundingMethodId}{
"accountId": 123456,
"fundingMethodId": 55182,
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"balanceAmount": {
"number": 950.00,
"currency": "USD"
},
"timeUpdated": 1745448383244
}
Partner Notes:
- Account-level
GET /balancereturns200with an empty list before issuance.- Scoped
GET /balance/{fundingMethodId}returns404when the ID is missing, not on the account, or not a Virtual Card funding method.- Path parameter is the Veem
fundingMethodId(number), not an external card token.onBehalfAccountIdis an optional query string parameter for partner-on-behalf calls.
Get Virtual Card Details
Details endpoint provides operational metadata for an issued Virtual Card.
GET /v1.2/virtual-card/details
GET /v1.2/virtual-card/details/{fundingMethodId}{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"fundingMethodId": 55182,
"isoCurrencyCode": "USD",
"status": "Active"
}
Partner Notes:
- Public details do not expose PAN, last four, CVV, or raw card-network payloads.
- Application/funding-method lifecycle status remains on
GET /v1.2/virtual-card; detailsstatusis the issued-card operational status.- Account-level details returns
200with an empty list before issuance; scoped details returns404for invalid IDs.
Get Virtual Card Transactions
Returns date-bounded transaction history for one Virtual Card.
GET /v1.2/virtual-card/transactions/{fundingMethodId}?startDate=yyyy-MM-dd&endDate=yyyy-MM-dd&pageNumber=0&pageSize=25{
"content": [
{
"id": 1001,
"accountId": 123456,
"fundingMethodId": 55182,
"paymentId": 477206,
"transactionId": 90001,
"amount": {
"number": 25.00,
"currency": "USD"
},
"type": "SPEND",
"status": "COMPLETE",
"createdAt": 1745448383244,
"updatedAt": 1745448383244,
"completedAt": 1745448383244,
"date": 1745448383244,
"originatorName": "Acme Store",
"additionalInformation": "Card purchase"
}
],
"number": 0,
"size": 25,
"totalElements": 1,
"totalPages": 1
}Query parameters
| Parameter | Mandatory | Description |
|---|---|---|
fundingMethodId | Yes | Path parameter. Partner card identifier from virtualCards[]. |
startDate | Yes | yyyy-MM-dd |
endDate | Yes | yyyy-MM-dd |
pageNumber | No | Default 0 |
pageSize | No | Default 25, max 25 |
onBehalfAccountId | No | Partner-on-behalf referred account ID |
Transaction types
SPEND, REFUND, FEE, DEPOSIT, WITHDRAWAL, CASH_BACK, TRANSFER, OTHER
Transaction statuses
IN_PROGRESS, COMPLETE, CLOSED
Amount and currency semantics
Rows use the amount and currency for the side of the movement that affects the card balance:
- Incoming card movement: receiving-side amount/currency
- Outgoing card movement: sending-side amount/currency
Responses do not expose external processor names, external transaction tokens, PAN-like values, or raw card-network JSON.
Fund Virtual Card from Wallet
Load funds from a referred-account Veem Wallet onto an issued Virtual Card.
POST /v1.2/payments/wallet/withdraw{
"onBehalfAccountId": 123456,
"amount": {
"number": 100.00,
"currency": "USD"
},
"toFundingMethodType": "VirtualCard",
"toFundingMethodId": 55182,
"fromWalletId": 90001
}Notes:
toFundingMethodTypemust beVirtualCardtoFundingMethodIdis required (no default-first-card behavior)- Phase 1 funding is USD; existing Wallet withdraw minimums apply
- Response is the standard public payment response; confirm with payment polling and balance reads
Fund Virtual Card from Bank
Load funds from a referred-account linked Bank funding method onto an issued Virtual Card.
POST /v1.2/payments/withdraw{
"onBehalfAccountId": 123456,
"amount": {
"number": 100.00,
"currency": "USD"
},
"fromFundingMethodType": "Bank",
"fromFundingMethodId": 175448,
"toFundingMethodType": "VirtualCard",
"toFundingMethodId": 55182
}Notes:
fromFundingMethodTypemust beBankfromFundingMethodIdis the linked Bank funding method ID on the referred accounttoFundingMethodTypemust beVirtualCardtoFundingMethodIdis required (no default-first-card behavior)- Funding is USD; existing Payment withdraw minimums apply
- Response is the standard public payment response; confirm with payment polling and balance reads
- This is a direct Bank → Virtual Card load. You do not need to fund a Wallet first.
Withdraw from Virtual Card to Wallet
Unload funds from an issued Virtual Card into a Wallet.
POST /v1.2/payments/withdraw{
"onBehalfAccountId": 123456,
"amount": {
"number": 50.00,
"currency": "USD"
},
"fromFundingMethodType": "VirtualCard",
"fromFundingMethodId": 55182,
"toFundingMethodType": "Wallet",
"toFundingMethodId": 90001
}Notes:
fromFundingMethodType/fromFundingMethodIdare required- Destination must be explicit: Wallet (
toFundingMethodType+toFundingMethodId) or partner aggregated wallet sweep (sweepToWalletId) - Bank, card, VBA, and other destinations are out of scope for this Virtual Card unload path in Phase 1
Send Payment from Virtual Card
Use an issued Virtual Card as the payer funding method on existing payment routes:
POST /v1.2/payments
POST /v1.2/claimless-payments
POST /v1.2/invoices/claimSet payer fundingMethod.type to VirtualCard and pass an explicit fundingMethod.id. There is no default-first-card behavior.
Create Invoice Settling to Virtual Card
POST /v1.2/invoicesResolve the payee settlement destination to an issued Virtual Card funding method through an explicit fundingMethod or the payee account default credit method. Existing sweep precedence is unchanged:
sweepToWalletId -> sweepToVBAId -> explicit fundingMethod -> default credit methodPhase 1 does not add sweepToVirtualCardId.
Virtual Card Webhooks
For creating a webhook to be notified when a Virtual Card event occurs, use:
POST /v1.2/webhooksIn addition to the webhooks described on the Webhook Notifications page, you can subscribe to the following Virtual Card events:
| Event | Description |
|---|---|
VIRTUAL_CARD_APPLICATION_STATUS_UPDATED | Notifies when the Virtual Card application changes public status |
VIRTUAL_CARD_BALANCE_UPDATED | Notifies after the canonical spendable balance is refreshed |
VIRTUAL_CARD_TRANSACTION | Notifies for card-network activity (spend, refund, fee, external deposit, etc.) |
Subscribe using the same webhook create API as VBA. Each Virtual Card event is independently subscribable. There is no genericVIRTUAL_CARD_CHANGEDevent.
Virtual Card Application Status Webhook
{
"callbackURL": "https://webhook-test.com/example",
"event": "VIRTUAL_CARD_APPLICATION_STATUS_UPDATED"
}{
"type": "VIRTUAL_CARD_APPLICATION_STATUS_UPDATED",
"data": "{\"accountId\":123456,\"status\":\"Completed\",\"updatedAt\":1745448383244}"
}{
"accountId": 123456,
"status": "Completed",
"reason": null,
"updatedAt": 1745448383244
}| Field | Description | Type |
|---|---|---|
accountId | Account ID | Long |
status | Public application status (Initiated, Pending, Approved, Completed, Rejected) | string |
reason | Optional sanitized reason | string |
updatedAt | Update timestamp | Long / date-time |
Published on initial application creation and every persisted public-status change. Unchanged saves do not emit.
Virtual Card Balance Webhook
{
"callbackURL": "https://webhook-test.com/example",
"event": "VIRTUAL_CARD_BALANCE_UPDATED"
}{
"type": "VIRTUAL_CARD_BALANCE_UPDATED",
"data": "{\"accountId\":123456,\"fundingMethodId\":55182,\"balance\":{\"amount\":950.00,\"currency\":\"USD\"},\"updatedAt\":1745448383244}"
}{
"accountId": 123456,
"fundingMethodId": 55182,
"balance": {
"amount": 950.00,
"currency": "USD"
},
"updatedAt": 1745448383244
}Emitted only after Veem successfully refreshes the canonical spendable balance. Independent of the transaction or payment that caused the refresh.
Virtual Card Transaction Webhook
{
"callbackURL": "https://webhook-test.com/example",
"event": "VIRTUAL_CARD_TRANSACTION"
}{
"type": "VIRTUAL_CARD_TRANSACTION",
"data": "{\"id\":1001,\"transactionId\":90001,\"accountId\":123456,\"fundingMethodId\":55182,\"amount\":{\"number\":25.00,\"currency\":\"USD\"},\"type\":\"SPEND\",\"status\":\"COMPLETE\",\"createdAt\":1745448383244,\"updatedAt\":1745448383244,\"completedAt\":1745448383244,\"originatorName\":\"Acme Store\",\"additionalInformation\":\"Card purchase\"}"
}{
"id": 1001,
"transactionId": 90001,
"accountId": 123456,
"fundingMethodId": 55182,
"amount": {
"number": 25.00,
"currency": "USD"
},
"type": "SPEND",
"status": "COMPLETE",
"createdAt": 1745448383244,
"updatedAt": 1745448383244,
"completedAt": 1745448383244,
"originatorName": "Acme Store",
"additionalInformation": "Card purchase"
}Notes:
- Delivered for card-network activity (card spend, refund, fee, externally originated deposit)
- Wallet↔card funding and card→Wallet withdrawals initiated through Veem Payment APIs are owned by the existing payment-status webhook and do not also emit
VIRTUAL_CARD_TRANSACTION - Exact redeliveries with the same transaction identity and status are suppressed
- Payload omits external processor fields and
paymentId - Transaction types/statuses match the transaction read API
Common Errors
| HTTP | When |
|---|---|
400 | Invalid request body, ineligible account, invalid funding method on payment routes |
404 | Unknown/invalid scoped card id on balance/details/transactions, or auth/account resolution failure |
409 | Reused X-Request-Id with a different payload / conflict |
5xx | Unexpected server error (for example submit succeeded but application read-back failed) |
| ) |
Updated 2 days ago
