---
title: "Create a virtual account payment method"
method: POST
path: "/api/v1/payment-method/virtual-account"
tags: ["Payment Methods"]
---

# Create a virtual account payment method

`POST /api/v1/payment-method/virtual-account`

## Request

- Base URL: `https://sandbox.api.afriex.com`
- URL: `https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account`
- Auth: API key in header `x-api-key`
- TypeScript: <https://skmtc.dev/afriex/apis/afriex-business-api/docs/api/v1/payment-method/virtual-account/post.ts> (a function with typed inputs; its response is checked against the schema)

Creates a new virtual account for the resolved customer-or-owner and
currency. Pass `customerId` to mint the VA for a specific end-user;
omit it to mint one for the business owner.

**Supported currencies.** A virtual account for one of your customers
(`customerId` supplied) can be created in `NGN` or `GHS`. Any other
currency returns `422 UNSUPPORTED_VIRTUAL_ACCOUNT_CURRENCY`; create it for
the business itself by omitting `customerId`, which is how `KES` and
`USD` accounts are created.

Subject to a per-(business, customer, currency) limit. Requests that
would exceed the limit return `403 VIRTUAL_ACCOUNT_LIMIT_REACHED`.

**NGN and BVN.** A permanent `NGN` account (no `amount`) is opened
against the account holder's BVN. If the holder has none, the request
returns `VIRTUAL_ACCOUNT_BVN_REQUIRED`; pass `amount` to get a one-time
account instead, which does not need a BVN.

**Compliance review:** every currency requires a compliance-approved
application before issuance. The first request for a not-yet-approved
business/currency starts that review and returns
`VIRTUAL_ACCOUNT_PENDING_COMPLIANCE_REVIEW`; retry once approved.

**Accounts opened asynchronously:** for some currencies the issuing
bank opens the account after the request returns. The response is
then `201` with an empty `data` object, and the account is delivered
through the `PAYMENT_METHOD.CREATED` webhook once it is open.

**Label vs amount:** `label` groups static virtual accounts by
purpose (e.g. `SALES`, `OPERATIONS`). It is **mutually exclusive
with `amount`**, which routes to a dynamic (ephemeral) virtual
account. Supplying both is rejected at validation.

## Request body

- object
  - `currency` 'NGN' | 'GHS' | 'KES' | 'USD', required: The 3-letter ISO 4217 currency code for the virtual account. The listed values are the supported currencies; other valid currency codes pass validation and fail later in the request.
  - `customerId` string: Optional customer ID. When supplied, the VA is created for that customer. When omitted, it is created for the business owner.
  - `amount` number: Optional amount for a dynamic (ephemeral) virtual account. Mutually exclusive with `label`.
  - `label` 'SALES' | 'OPERATIONS' | 'PAYROLL' | 'COLLECTIONS' | 'VENDOR_PAYMENTS' | 'TAX' | 'REFUNDS' | 'MARKETING' | 'TREASURY' | 'GENERAL': Optional label that groups static virtual accounts by purpose. Mutually exclusive with `amount`.

## Response `201`

Virtual account created successfully, or (empty `data`) requested
and pending with the issuing bank.

- object
  - `data` union
    - PaymentMethod: A payment method. Empty fields are omitted. Account-shaped channels (BANK_ACCOUNT, MOBILE_MONEY, SWIFT, etc.) populate accountName, accountNumber, and institution; the CARD channel instead populates last4, brand, expiration, and cardName.
      - `paymentMethodId` string: The unique identifier for the payment method.
      - `customerId` string: The unique identifier of the customer.
      - `reference` string: Identifier to reconcile incoming deposits against.
      - `institution` PaymentMethodInstitution: On a payment method, only `institutionId`, `institutionName`, `institutionCode`, `institutionAddress` and the correspondent bank fields are returned. The address components and `institutionBranch` are accepted when you create one but are not returned on it; `institutionBranch` is returned by the institution list.
        - `institutionId` string: The unique identifier of the bank or mobile money provider if required.
        - `institutionName` string: The name of the bank or mobile money provider if required.
        - `institutionCode` string: The code of the bank or mobile money provider.
        - `institutionAddress` string: The address of the bank or mobile money provider if required.
        - `institutionAddressLine1` string: Optional. First line of `institutionAddress`, for SWIFT payment methods. Used ahead of `recipientAddressLine1`.
        - `institutionAddressCity` string: Optional. City of `institutionAddress`, for SWIFT payment methods.
        - `institutionAddressState` string: Optional. State of `institutionAddress`, for SWIFT payment methods.
        - `institutionAddressZip` string: Optional. Postal or ZIP code of `institutionAddress`, for SWIFT payment methods.
        - `institutionAddressCountry` string: Optional. Country of `institutionAddress`, for SWIFT payment methods.
        - `institutionBranch` string: The branch of the bank. Required when creating a BANK_ACCOUNT payment method in CNY. Bank lists return it too, often as an empty string.
        - `correspondentBankName` string: The correspondent (intermediary) bank name. Mandatory for USD (SWIFT) payout payment methods; required to produce settlement advices/invoices.
        - `correspondentBankAccountNumber` string: The correspondent (intermediary) bank account number. Mandatory for USD (SWIFT) payout payment methods; required to produce settlement advices/invoices.
      - `transaction` PaymentMethodTransaction
        - `transactionInvoice` string: The invoice filename (S3 object key) for the transaction, as returned by the media upload endpoint.
        - `transactionNarration` string: The optional reason for the transaction.
      - `recipient` PaymentMethodRecipient: On a payment method, only `recipientEmail`, `recipientPhone`, `recipientAddress` and `recipientName` are returned. The other fields are accepted when you create one but are not returned on it.
        - `recipientEmail` string, email: The email address of the recipient if needed.
        - `recipientPhone` string: The phone number of the recipient in E.164 format.
        - `recipientAddress` string: The address of the recipient if needed.
        - `recipientAddressLine1` string: Optional. First line of `recipientAddress`. Used on SWIFT payment methods when `institutionAddressLine1` is not sent.
        - `recipientAddressCity` string: Optional. City of `recipientAddress`.
        - `recipientAddressState` string: Optional. State of `recipientAddress`.
        - `recipientAddressZip` string: Optional. Postal or ZIP code of `recipientAddress`.
        - `recipientAddressCountry` string: Optional. Country of `recipientAddress`.
        - `recipientIdType` 'national_id' | 'passport' | 'drivers_license' | 'voters_card' | 'residence_permit' | 'bvn' | 'tax_id': The type of the recipient's ID. Needed, with `recipientIdNumber`, for BANK_ACCOUNT payment methods in CNY, where it is recorded as the recipient's KYC.
        - `recipientIdNumber` string: The number of the ID named by `recipientIdType`. Needed for BANK_ACCOUNT payment methods in CNY.
        - `recipientUpiId` string: The recipient's UPI ID. Used on UPI payment methods; when omitted, `accountNumber` is used.
        - `recipientName` string: The name of the recipient if needed.
      - `channel` 'BANK_ACCOUNT' | 'MOBILE_MONEY' | 'SWIFT' | 'INTERAC' | 'UPI' | 'WE_CHAT' | 'ALIPAY' | 'CARD' | 'CRYPTO' | 'VIRTUAL_BANK_ACCOUNT' | 'POOL_ACCOUNT' | 'ACH_BANK_ACCOUNT' | 'PAYBILL_TILL' | 'RFP' | 'VIRTUAL_CARD': The payment method channel.
      - `countryCode` string: The ISO 3166-1 alpha-2 country code.
      - `currency` string: The payment method's currency (ISO 4217). Use this instead of inferring currency from countryCode, which is ambiguous (e.g. a USD account in a non-US country).
      - `capabilities` string[]: The operations this payment method is enabled for (e.g. DEPOSIT, WITHDRAW). Use it to select deposit-capable vs withdraw-capable methods. Also filterable via the `capabilities` query parameter.
      - `accountName` string: Name of the account holder. Present for account-shaped channels.
      - `accountNumber` string: Account number. Present for account-shaped channels, except INTERAC, which returns none. For UPI it carries the recipient phone number, not the UPI ID.
      - `routingNumber` string: Bank routing number. Present for account-shaped channels that carry one (e.g. ACH bank accounts).
      - `bankAddress` PaymentMethodBankAddress: The issuing bank's postal address, populated per account by the provider that provisioned it. Present on virtual accounts and other account-shaped payment methods whose provider supplies it; omitted otherwise. This is the bank's address, not the account holder's.
        - `line1` string: First line of the bank's street address.
        - `line2` string: Second line of the bank's street address, when present.
        - `city` string: City.
        - `state` string: State or region, when present.
        - `zip` string: Postal or ZIP code.
        - `country` string: Country, as supplied by the provider.
      - `status` 'active' | 'pending' | 'deleted' | 'expired' | 'blocked': Lifecycle status of the payment method.
      - `last4` string: Last 4 digits of the card. CARD channel only.
      - `brand` 'Visa' | 'MasterCard' | 'Discover' | 'American Express' | 'JCB' | 'Diners Club' | 'Eftpos Australia' | 'UnionPay' | 'Unknown': Card brand. CARD channel only.
      - `expiration` object: Card expiration. CARD channel only.
        - `month` integer
        - `year` integer
      - `cardName` string: Name on the card. CARD channel only.
      - `expiresInMinutes` integer: Minutes until a dynamic virtual account expires, when applicable.
      - `amount` union: The amount stored with the payment method, as you sent it (a number or a numeric string), or the requested amount for a dynamic virtual account, when applicable.
        - number
        - string
      - `extra` object: Additional channel-specific properties.
      - `meta` object: The meta data you attached to the payment method when you created it.
    - object: Empty while the account is being opened.

## Other responses

- `400`: Invalid request (e.g. `label` and `amount` both supplied), or a permanent `NGN` account requested for a holder without a BVN.
- `401`: Unauthorized - Invalid business API key.
- `403`: The per-(business, customer, currency) virtual account limit is reached, or the business is still pending compliance review for virtual account issuance.
- `422`: The requested currency is not available to the resolved account holder, or the account type (permanent, or one-time with `amount`) is not offered for this currency.
- `500`: Server error.
- `503`: Temporarily unavailable: an upstream service failed or timed out, or the API is momentarily offline. Retry with backoff and honour the Retry-After header when present.

---

[API](https://skmtc.dev/afriex/apis/afriex-business-api.md) - [All operations](https://skmtc.dev/afriex/apis/afriex-business-api/llms.txt) - [OpenAPI document](https://skmtc.dev/afriex/apis/afriex-business-api/revisions/d94198b6c256?raw)
