Concepts
A virtual account is a 10-digit NUBAN that any bank in Nigeria can pay into over NIP, but that holds no money of its own. When a transfer lands on it, the bank routes the credit to a real account you own, the settlement account, and tells you it happened. You hand a different number to each payer, invoice or order, and the number itself tells you what the payment was for.
How a number reaches you
Section titled “How a number reaches you”- The bank carves a band (a contiguous block of 10-digit numbers) out of its NIBSS-registered range and assigns it to your organisation. A band belongs to one customer record: yours, or a sub-agent’s.
- You ask the bank for a number from the band, with or without an expiry, or, on a partner-maintained band, you allocate numbers yourself.
- A payer at any bank does a name enquiry on the number. The bank answers with the name you set, or asks your endpoint.
- The credit arrives. The bank validates it against the number’s rules, withholds any band fee, credits the settlement account with the net amount, and books a transaction.
- You are notified: a signed webhook on a bank-issued band, a POST to your endpoint on a partner-maintained band.
- The next business day NIBSS settles the money to the bank. That step is between the bank and NIBSS; your account was credited at step 4.
Bank-issued and partner-maintained bands
Section titled “Bank-issued and partner-maintained bands”The bank configures each band in one of two modes. You do not choose per number; you find out which you have from the Virtual accounts page of the portal, where a bank-issued band is shown as LOCAL and a partner-maintained band as REMOTE.
| Bank-issued (LOCAL) | Partner-maintained (REMOTE) | |
|---|---|---|
| Who allocates numbers | The bank, when you call create | You, from the band’s start to end |
| Does the bank hold a record per number | Yes, with status, expiry and amount rules | No. It routes by containment: any number inside the band is yours |
| Name enquiry | Answered by the bank from the record | Proxied to your validation endpoint |
| Where the money lands | The settlement account chosen at creation | The band’s settlement account, or an account you nominate at name enquiry |
| How you are told | transaction.completed webhook on your subscription |
HTTP POST to your payment notification endpoint |
| Single-use, TTL, exact amount | Per number | Not available. Band-wide fixed or minimum amount, plus a per-number amount you can state at name enquiry |
The four pages under Bank-issued numbers are the API for a bank-issued band. A partner-maintained band has no create call: your numbers exist the moment the band does. How partner-maintained numbers work is the overview of that model.
Expiring and non-expiring numbers
Section titled “Expiring and non-expiring numbers”On a bank-issued band every number is the same kind of thing: a record with a name, a settlement account, and rules about what it accepts. The two create calls differ only in which rules they set.
POST /virtual_account/create(Create a virtual account) takes an optional time-to-live, an expected amount, a single-use flag and your reference. With nottlthe number never expires; withmulti: trueit takes any number of payments.POST /createFVA(Create a non-expiring virtual account) is the same number with no time-to-live and multi-use already decided, carrying the payer’s first name, last name and phone as the name shown at name enquiry. It is not a different kind of account.
A number’s status is ACTIVE, then USED (single-use, after the first payment) or EXPIRED (TTL elapsed, or you expired it). A non-expiring, multi-use number stays ACTIVE until you expire it.
Single-use, TTL and exact amount
Section titled “Single-use, TTL and exact amount”- Single-use (
multiomitted orfalse): the first accepted payment flips the number toUSED. A second transfer to it is refused at name enquiry and at credit. Multi-use (multi: true): the number staysACTIVEand takes any number of payments. - TTL (
ttl, minutes): after it elapses the number isEXPIRED. A sweep retires lapsed numbers every five minutes, but the payment-time check uses the clock, so a payment that arrives after expiry is refused even before the sweep runs. Omitttlfor no expiry. - Exact amount (
amountwithexact_amount: true, the default when you sendamount): a transfer for any other value is refused with NIBSS13, before the money moves. Sendexact_amount: falseto treatamountas a note.
Refusals happen at name enquiry or at the credit acknowledgement, so the payer’s bank shows its own customer a failure and no money moves. There is nothing to reverse.
The settlement account
Section titled “The settlement account”Every number has one. For a bank-issued number it is chosen when you create it, in this order:
beneficiary_accountin the request, if you send one.- The account the bank linked to the band, if it linked one.
- The account in your
X-Agent-Accountheader. - The oldest account of the customer who owns the band.
Payments to the number credit that account with the net amount. A partner-maintained band settles to its own settlement account unless you nominate another allow-listed one at name enquiry.
X-Agent-Account on virtual account calls
Section titled “X-Agent-Account on virtual account calls”The agent endpoints take X-Agent-Account: <10-digit account number>. On the create calls it decides two things: whose band the number is allocated from (the customer who owns that account must have a bank-issued band with capacity), and the default settlement account when you send no beneficiary_account and the band has no linked account.
The account must be one the ownership guard lets you read: your own organisation’s account, or the transaction account of a sub-agent you onboarded, with sub-agent access enabled on your client. Anything else is refused with 0012. See Ownership model.
The expire and check calls do not read the header.
The bank may price a band: a flat amount, a percentage, a percentage with a cap, or a floor. The fee is withheld from each inflow and the settlement account receives the net. It is paid to a fee account the bank registered for the band, which can be one of your own accounts, or kept by the bank when none is registered. A band with no pricing withholds nothing. The payment notification on a partner-maintained band reports the gross amount; the webhook on a bank-issued band reports the net with grossAmount and feeAmount beside it.
Native endpoints
Section titled “Native endpoints”The BankCore-native virtual account API also accepts partner tokens on two routes: POST /api/virtual-accounts (scope accounts:create, body customerId, accountName, ttl, amount, exactAmount, multiUse, externalRef, beneficiaryAccountNumber) and GET /api/virtual-accounts with GET /api/virtual-accounts/by-account/{accountNumber} (scope accounts:read). They return the full record as JSON, including status, expiryDate and paymentRef. The transaction history, expiry and webhook-resend routes under /api/virtual-accounts are staff-only.
In the portal
Section titled “In the portal”Virtual accounts lists every number issued from your bands with its effective status, settlement account, expiry, amount rule and your reference. Inflows lists the payments that landed on a band and whether you were notified.