Create Virtual Account
curl --request POST \
--url https://api.usezentra.com/api/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "<string>",
"account_name": "<string>",
"provider": "<string>",
"account_type": "<string>",
"metadata": {},
"bvn": "<string>",
"country": "<string>",
"currency": "<string>",
"request_id": "<string>",
"preferred_bank": "<string>"
}
'import requests
url = "https://api.usezentra.com/api/v1/virtual-accounts"
payload = {
"customer_id": "<string>",
"account_name": "<string>",
"provider": "<string>",
"account_type": "<string>",
"metadata": {},
"bvn": "<string>",
"country": "<string>",
"currency": "<string>",
"request_id": "<string>",
"preferred_bank": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customer_id: '<string>',
account_name: '<string>',
provider: '<string>',
account_type: '<string>',
metadata: {},
bvn: '<string>',
country: '<string>',
currency: '<string>',
request_id: '<string>',
preferred_bank: '<string>'
})
};
fetch('https://api.usezentra.com/api/v1/virtual-accounts', 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://api.usezentra.com/api/v1/virtual-accounts",
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([
'customer_id' => '<string>',
'account_name' => '<string>',
'provider' => '<string>',
'account_type' => '<string>',
'metadata' => [
],
'bvn' => '<string>',
'country' => '<string>',
'currency' => '<string>',
'request_id' => '<string>',
'preferred_bank' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.usezentra.com/api/v1/virtual-accounts"
payload := strings.NewReader("{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.usezentra.com/api/v1/virtual-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usezentra.com/api/v1/virtual-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"error": {
"code": "VALIDATION_ERROR",
"message": "customer_id is required",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
{
"error": {
"code": "FAILED_PRECONDITION",
"message": "Provider routing unavailable",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
Create Virtual Account
POST /api/v1/virtual-accounts - Create a tenant-scoped virtual account
POST
/
api
/
v1
/
virtual-accounts
Create Virtual Account
curl --request POST \
--url https://api.usezentra.com/api/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"customer_id": "<string>",
"account_name": "<string>",
"provider": "<string>",
"account_type": "<string>",
"metadata": {},
"bvn": "<string>",
"country": "<string>",
"currency": "<string>",
"request_id": "<string>",
"preferred_bank": "<string>"
}
'import requests
url = "https://api.usezentra.com/api/v1/virtual-accounts"
payload = {
"customer_id": "<string>",
"account_name": "<string>",
"provider": "<string>",
"account_type": "<string>",
"metadata": {},
"bvn": "<string>",
"country": "<string>",
"currency": "<string>",
"request_id": "<string>",
"preferred_bank": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
customer_id: '<string>',
account_name: '<string>',
provider: '<string>',
account_type: '<string>',
metadata: {},
bvn: '<string>',
country: '<string>',
currency: '<string>',
request_id: '<string>',
preferred_bank: '<string>'
})
};
fetch('https://api.usezentra.com/api/v1/virtual-accounts', 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://api.usezentra.com/api/v1/virtual-accounts",
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([
'customer_id' => '<string>',
'account_name' => '<string>',
'provider' => '<string>',
'account_type' => '<string>',
'metadata' => [
],
'bvn' => '<string>',
'country' => '<string>',
'currency' => '<string>',
'request_id' => '<string>',
'preferred_bank' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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://api.usezentra.com/api/v1/virtual-accounts"
payload := strings.NewReader("{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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://api.usezentra.com/api/v1/virtual-accounts")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.usezentra.com/api/v1/virtual-accounts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"customer_id\": \"<string>\",\n \"account_name\": \"<string>\",\n \"provider\": \"<string>\",\n \"account_type\": \"<string>\",\n \"metadata\": {},\n \"bvn\": \"<string>\",\n \"country\": \"<string>\",\n \"currency\": \"<string>\",\n \"request_id\": \"<string>\",\n \"preferred_bank\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"error": {
"code": "VALIDATION_ERROR",
"message": "customer_id is required",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
{
"error": {
"code": "FAILED_PRECONDITION",
"message": "Provider routing unavailable",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
Create a dedicated virtual account for a customer.
Endpoint
POST /api/v1/virtual-accounts
Request Body
string
required
ID of the customer this account belongs to.
string
required
Name to display on the account.
string
Optional provider override. If omitted, the gateway defaults this to
auto and internal routing selects the provider.string
Optional account type. Supported values depend on your tenant configuration. Common values are
standard and dedicated.object
Optional metadata stored with the account.
string
Optional BVN when required by your provider or compliance flow.
string
Optional ISO country code used for provider routing.
string
Optional currency code used for provider routing.
string
Optional request correlation ID.
string
Optional preferred bank hint for providers that support bank selection.
Response Fields
string
Unique identifier for the virtual account.
string
Tenant that owns the account.
string
Customer assigned to the account.
string
Account number returned by the underlying provider.
string
Name on the account.
string
Provider bank code or slug.
string
Human-readable bank name.
string
Provider selected for the account.
string
Current account status. Typical values are
active, frozen, closed, or expired.string
Account type stored by the service.
number
Total inbound value recorded on the account, in minor units.
number
Number of inbound credits recorded on the account.
object | null
Account metadata.
string | null
Provider expiry time when the allocated account is time-bound.
string
ISO 8601 creation timestamp.
string
ISO 8601 last-update timestamp.
Example Request
const account = await client.virtualAccounts.create({
customerId: 'cus_1234567890',
accountName: 'John Doe Collections',
preferredBank: 'wema-bank',
metadata: {
purpose: 'collections',
tier: 'premium'
}
});
console.log(account.accountNumber);
account = client.virtual_accounts.create(
customer_id='cus_1234567890',
account_name='John Doe Collections',
preferred_bank='wema-bank',
metadata={
'purpose': 'collections',
'tier': 'premium'
}
)
print(account.account_number)
$account = $client->virtualAccounts->create([
'customer_id' => 'cus_1234567890',
'account_name' => 'John Doe Collections',
'preferred_bank' => 'wema-bank',
'metadata' => [
'purpose' => 'collections',
'tier' => 'premium'
]
]);
print_r($account);
curl -X POST https://api.usezentra.com/api/v1/virtual-accounts \
-H "Authorization: Bearer YOUR_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_1234567890",
"account_name": "John Doe Collections",
"preferred_bank": "wema-bank",
"metadata": {
"purpose": "collections",
"tier": "premium"
}
}'
Example Response
{
"data": {
"id": "va_abc123xyz",
"tenant_id": "tenant_123",
"customer_id": "cus_1234567890",
"account_number": "0123456789",
"account_name": "John Doe Collections",
"bank_code": "wema-bank",
"bank_name": "Wema Bank",
"provider": "paystack_dva",
"status": "active",
"account_type": "standard",
"total_received_minor": 0,
"transaction_count": 0,
"metadata": {
"purpose": "collections",
"tier": "premium"
},
"expires_at": null,
"created_at": "2026-03-07T10:30:00Z",
"updated_at": "2026-03-07T10:30:00Z"
},
"meta": {
"timestamp": "2026-03-07T10:30:00Z",
"requestId": "req_123"
}
}
Error Responses
{
"error": {
"code": "VALIDATION_ERROR",
"message": "customer_id is required",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
{
"error": {
"code": "FAILED_PRECONDITION",
"message": "Provider routing unavailable",
"details": null
},
"meta": {
"path": "/api/v1/virtual-accounts",
"method": "POST"
}
}
Notes
bank_code is an output field. The reviewed create endpoint does not require a bank code; use preferred_bank only when you need to bias provider selection.Amounts are always returned in integer minor units. For virtual accounts, the running inbound total is
total_received_minor.Next Steps
Get Account
Retrieve account details
List Accounts
View all accounts
Handle Webhooks
Process account credits
Close Account
Close an account
Was this page helpful?