curl --request POST \
--url https://api.bachs.io/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"currency": "NGN"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({currency: 'NGN'})
};
fetch('https://api.bachs.io/v1/virtual-accounts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/virtual-accounts"
payload = { "currency": "NGN" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/virtual-accounts"
payload := strings.NewReader("{\n \"currency\": \"NGN\"\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))
}{
"id": "va_8Hs2kQ4mZpXv",
"currency": "NGN",
"account_number": "9902847361",
"bank_name": "Example Bank",
"bank_code": "000",
"status": "active",
"created_at": "2026-09-22T09:14:02.000Z"
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Submission has missing or invalid required fields",
"error_code": "VALIDATION_FAILED",
"missing_fields": [
"company.registration_number"
],
"missing_documents": []
}{
"detail": "Rate limit exceeded. Please retry after a few seconds.",
"error_code": "TOO_MANY_REQUESTS"
}{
"detail": "An unexpected error occurred. Please try again later.",
"error_code": "INTERNAL_SERVER_ERROR"
}Create a virtual account
Creates a fixed bank account number in one currency, or returns the virtual account that already exists for that currency. See Virtual accounts for capability setup, requirements, deposits, and fees.
curl --request POST \
--url https://api.bachs.io/v1/virtual-accounts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"currency": "NGN"
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({currency: 'NGN'})
};
fetch('https://api.bachs.io/v1/virtual-accounts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.bachs.io/v1/virtual-accounts"
payload = { "currency": "NGN" }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.bachs.io/v1/virtual-accounts"
payload := strings.NewReader("{\n \"currency\": \"NGN\"\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))
}{
"id": "va_8Hs2kQ4mZpXv",
"currency": "NGN",
"account_number": "9902847361",
"bank_name": "Example Bank",
"bank_code": "000",
"status": "active",
"created_at": "2026-09-22T09:14:02.000Z"
}{
"detail": "Invalid request parameters",
"error_code": "VALIDATION_ERROR",
"errors": [
{
"field": "amount",
"message": "Amount must be a positive decimal string",
"type": "value_error"
}
]
}{
"detail": "Invalid API key",
"error_code": "UNAUTHORIZED"
}{
"detail": "API key does not have permission for this operation",
"error_code": "FORBIDDEN"
}{
"detail": "Submission has missing or invalid required fields",
"error_code": "VALIDATION_FAILED",
"missing_fields": [
"company.registration_number"
],
"missing_documents": []
}{
"detail": "Rate limit exceeded. Please retry after a few seconds.",
"error_code": "TOO_MANY_REQUESTS"
}{
"detail": "An unexpected error occurred. Please try again later.",
"error_code": "INTERNAL_SERVER_ERROR"
}Authorizations
Bearer token authentication. Pass your API key as Authorization: Bearer sk_.... See Authentication for keys, scopes, and sandbox vs production.
Body
Three-letter ISO 4217 code for the money the account accepts, e.g. NGN. NGN is the only currency issued today; any other value is rejected with VIRTUAL_ACCOUNT_CURRENCY_NOT_SUPPORTED. There is no default, because the currency decides what the number can receive.
3"NGN"
Response
Virtual account created, or the existing virtual account for the requested currency.
A fixed bank account number belonging to your platform or a connected account. It does not expire and can receive deposits at any time.
Unique identifier for the virtual account, prefixed va_. It appears at payment_method_details.bank_transfer.virtual_account.id on each payment received through this account.
"va_8Hs2kQ4mZpXv"
Three-letter ISO 4217 code for the money this account accepts, e.g. NGN. Money sent in any other currency cannot reach it.
"NGN"
The account number the sender enters in their banking app. Show it with bank_name so the sender can select the correct destination.
"9902847361"
The bank that issued the number, as the sender sees it in their banking app. Read it from this field rather than storing one name, because accounts created later can be issued by a different bank.
"Example Bank"
Whether the virtual account is in use. active: the number appears in reads and accepts deposits. inactive: the number no longer appears in reads, but deposits sent to it are still received.
active, inactive "active"
ISO 8601 timestamp of when the account number was issued.
"2026-09-22T09:14:02.000Z"
The issuing bank's code, for apps that select a bank by code rather than by name. null when the issuing bank gives us none.
"000"

