NGN is the only currency issued today.
These calls act on the account named in X-Account-Id, or on your own account when you omit it. See Acting as an account.
Turn it on
A virtual account requires thevirtual_accounts capability and the account representative’s BVN. Virtual accounts are rolling out gradually, so contact hello@bachs.io to request access for your platform or a connected account.
1
Request access
Ask Bachs to enable
virtual_accounts for the account. Include the account ID when you request access for a connected account.2
Read what it needs
Requesting the capability puts a requirement on the account. Read
requirements.entries[] on the account, or wait for account.updated to tell you it changed. See Requirements.3
Submit the representative's BVN
Submit it as a
persons entry with relationship.representative: true and an id_numbers entry of type bvn. This is a requirements submission through the API, the same call you use for every other requirement field. See Submitting.The issuing bank will not create a number without this. It has to be the BVN of the person marked as the account’s representative, not any other person on the account.4
Wait for approval
The capability moves to
active on review. You are told through the capability.updated webhook, with data.capability set to virtual_accounts and data.status set to active. See capability.updated.5
Create the number
Approval does not create a number by itself. Call
POST /v1/virtual-accounts when you want one.Create the virtual account
POST /v1/virtual-accounts · scope virtual_accounts:write · full field reference
Name the currency. Calling this a second time with the same currency returns the account you already hold, so a retry after a timeout never leaves you with two numbers.
X-Account-Id and the call creates or reads back your own account instead of a connected one.
Do not treat a network or
5xx error on create as proof that no account was made. Call GET /v1/virtual-accounts before retrying, or retry the create, which returns the account you already hold rather than a second number.Read it back
GET /v1/virtual-accounts · scope virtual_accounts:read
Returns the virtual account for one currency. The currency defaults to NGN when you omit it. You get 404 with NOT_FOUND when the account has no virtual account in that currency.
Give the number out
Showaccount_number and bank_name together. For a connected account, the sender sees that account’s business name in their banking app, not your platform’s name.
Nothing is reserved in advance. There is no expected amount, and no reference for the sender to quote, so anything typed in the narration reaches you as plain text and nothing more. What identifies a deposit is the sender’s own bank details, which arrive with it.
Receive the deposit
A deposit arrives from a bank app, outside your integration, so the webhook is the only thing that tells you it happened. Each deposit becomes its own payment, and that payment succeeds on its own. You receivecollection.succeeded with checkout_id set to null, because no checkout created it.
collection.succeeded
checkout_id, reference, product_cart, and customer.id are null on every deposit. A bank transfer paid through a checkout carries all four instead.
An NGN virtual account deposit costs 1.5% of the amount received, capped at NGN 300. The fee is deducted before settlement, so the example above settles NGN 249,700 from an NGN 250,000 deposit. See Fees and Processing fees.
The money reaches the account’s payout schedule the same way as any other payment. See Payouts.
A deposit above the account’s limit waits before the money reaches its balance. An account has a limit on one deposit and a limit on a day’s deposits added together. A deposit over either one needs a manual review before the money goes into the balance. Deposit limits explains how limits work.
You cannot refund a deposit. The payment comes back with
is_refundable: false, and a refund request against it is rejected with 400. To return the money, send it back as a payout. Use payment_method_details.bank_transfer.sender_bank and sender_account_number as the payout destination details.
