Skip to main content
POST
/
api
/
v1
/
payment-method
/
virtual-account
Afriex SDK
const account = await afriex.paymentMethods.createVirtualAccount({
  currency: "USD",
  label: "SALES",
  customerId: "optional-customer-id",
});
curl --request POST \
--url https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account \
--header 'Content-Type: application/json' \
--header 'x-api-key: <api-key>' \
--data '
{
"currency": "NGN",
"customerId": "68e6717848e1f632e9686460",
"label": "SALES"
}
'
import requests

url = "https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account"

payload = {
"currency": "NGN",
"customerId": "68e6717848e1f632e9686460",
"label": "SALES"
}
headers = {
"x-api-key": "<api-key>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {'x-api-key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({currency: 'NGN', customerId: '68e6717848e1f632e9686460', label: 'SALES'})
};

fetch('https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'currency' => 'NGN',
'customerId' => '68e6717848e1f632e9686460',
'label' => 'SALES'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"x-api-key: <api-key>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account"

payload := strings.NewReader("{\n \"currency\": \"NGN\",\n \"customerId\": \"68e6717848e1f632e9686460\",\n \"label\": \"SALES\"\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("x-api-key", "<api-key>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account")
.header("x-api-key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"currency\": \"NGN\",\n \"customerId\": \"68e6717848e1f632e9686460\",\n \"label\": \"SALES\"\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://sandbox.api.afriex.com/api/v1/payment-method/virtual-account")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["x-api-key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"currency\": \"NGN\",\n \"customerId\": \"68e6717848e1f632e9686460\",\n \"label\": \"SALES\"\n}"

response = http.request(request)
puts response.read_body
{
  "data": {
    "paymentMethodId": "690cc5bbe2a1143ff6070119",
    "channel": "VIRTUAL_BANK_ACCOUNT",
    "customerId": "68e6717848e1f632e9686460",
    "institution": {
      "institutionName": "FIDELITY BANK"
    },
    "accountName": "Lily New",
    "accountNumber": "3820404958",
    "countryCode": "NG"
  }
}
Creates a new virtual account for the resolved customer-or-owner and currency. Pass customerId to mint the virtual account for a specific end-user, or omit it to mint one for the business owner. Important: Virtual accounts are only active in production and do not work on staging/dev.

Static vs dynamic virtual accounts

You choose between two flavours of virtual account by what you pass alongside currency and customerId.

Static (label)

Pass a label to group static virtual accounts by purpose. Static accounts are permanently assigned to the customer and never expire. The same account number remains valid for repeated funding. Allowed labels: SALES, OPERATIONS, PAYROLL, COLLECTIONS, VENDOR_PAYMENTS, TAX, REFUNDS, MARKETING, TREASURY, GENERAL. Use static accounts when you want stable, long-lived account numbers a customer can top up over time, organised by purpose.

Dynamic (amount)

Pass an amount to mint a dynamic (ephemeral) virtual account tied to that specific amount. Dynamic accounts expire after a short window. Use dynamic accounts when you need to collect a specific, known amount within a defined window, like a one-time payment for a particular order.
label and amount are mutually exclusive. Supplying both is rejected at validation.

BVN requirement for NGN static virtual accounts

For NGN static virtual accounts (label-based, Nigeria), the customer must have a Bank Verification Number (BVN) on file. This is a regulatory requirement for permanently assigned accounts in Nigeria. If the BVN is missing, the API returns an error. You can supply a BVN when creating a new customer or add it to an existing customer at any time using the Update Customer KYC endpoint.
NGN static virtual accounts will fail if the customer has not provided their BVN. This requirement does not apply to dynamic virtual accounts or to virtual accounts in other currencies.

Limits

Creation is subject to a per-(business, customer, currency) cap. Requests that would exceed the cap return VIRTUAL_ACCOUNT_LIMIT_REACHED with 400. Call List Virtual Accounts first to check what already exists for the customer.

Authorizations

x-api-key
string
header
required

Static business API key issued from the dashboard. A business can provision multiple API keys, each scoped to a configurable set of permissions (e.g. read transactions, create deposits, etc). Permissions are chosen per key at creation time in the dashboard and may be revoked by deleting the key. Requests made with a key that does not include the permission required by the target endpoint will be rejected with a 403 Forbidden response; an unrecognised, malformed or revoked key returns 401 Unauthorized. Manage your keys and their permissions under Developer → API keys in the dashboard.

Headers

x-api-version
string

API version in ISO 8601 format (e.g. 2025-12-28). Defaults to latest stable.

Body

application/json
currency
enum<string>
required

The 3-letter ISO 4217 currency code for the virtual account.

Available options:
USD,
NGN,
GBP,
EUR
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.

Required range: 0 <= x <= 5000000
label
enum<string>

Optional label that groups static virtual accounts by purpose. Mutually exclusive with amount.

Available options:
SALES,
OPERATIONS,
PAYROLL,
COLLECTIONS,
VENDOR_PAYMENTS,
TAX,
REFUNDS,
MARKETING,
TREASURY,
GENERAL

Response

Virtual account created successfully.

data
object

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.