Virtual accounts

Virtual accounts are generated account details (account number and bank) that allow Flutterwave merchants to receive payments from customers via bank transfer. Virtual accounts are currently only available in naira.

Virtual cards and virtual accounts are unrelated

Virtual accounts allow you to receive payments into your Flutterwave wallet via bank transfer, while virtual cards allow you to make online payments.

Virtual accounts are either dynamic (temporary) or static (permanent). A temporary account number expires after handling the specified number of transactions (the frequency parameter), while a static account number doesn't expire.

Temporary virtual accounts are useful for receiving one-off payments via bank transfer. When the customer is checking out, you generate a temporary virtual account with a fixed amount and provide to them. When they make payment, we'll send you a webhook and you can handle it.

Static virtual accounts can be useful if you're building an app with a wallet system. You can issue a virtual account number for each customer to use in funding their wallet:

  • You generate a virtual account for the customer with our API and provide them with the details
  • The customer transfers to their assigned account
  • We send you a webhook notifying you that we've received the payment
  • You credit the needed funds to the customer's wallet on your platform.

Creating a virtual account

You can create a virtual account with the create virtual account number endpoint, which is supported in our backend SDKs. At the very least, you'll need to specify the email to be associated with the account number (the customer's email). if you're creating a static virtual number, their bvn is also required:

// Install with: npm i flutterwave-node-v3

const Flutterwave = require('flutterwave-node-v3');
const flw = new Flutterwave(process.env.FLW_PUBLIC_KEY, process.env.FLW_SECRET_KEY);
const details = {
    email: ""
// Install with: composer require flutterwavedev/flutterwave-v3

$flw = new \Flutterwave\Rave(getenv('FLW_SECRET_KEY')); // Set `PUBLIC_KEY` as an environment variable
$virtualAccountsService = new \Flutterwave\VirtualAccount();
$details = [
    "email" => "",
$response = $virtualAccountsService->createVirtualAccount($details);
# Install with: gem install flutterwave_sdk

require 'flutterwave_sdk'

virtual_account_number =
details = {
  email: ""
response = virtual_account_number.create_virtual_account_number details
print response
# Install with: pip install rave_python

import os
from rave_python import Rave

rave = Rave(os.getenv("FLW_PUBLIC_KEY"), os.getenv("FLW_SECRET_KEY"))
res = rave.VirtualAccount.create({
    "email": ""
// Install with: go get

import (
var r = rave.Rave{
var virtualAccount = rave.Virtualaccount{
details := rave.CreateAcctData {
  Email: "",
err, response := virtualAccount.Create(details)
if err != nil {
curl --request POST '' \
  --header 'Authorization: Bearer YOUR_SECRET_KEY' \
  --header 'Content-Type: application/json' \
  --data-raw '{
    "email": ""
BVNs required for static virtual account

When creating a static virtual account, you'll need to provide a BVN. If you've created a static virtual account without a BVN in the past, you'll need to set a BVN. See more details in our announcement.

There are more options available when creating virtual accounts. You can also set:

  • amount: the exact amount to be collected per transaction
  • frequency: the number of times the account can be used before expiring
  • narration: the name that should be shown when the account details are fetched
  • tx_ref: the transaction reference that should be returned whenever a transfer is made into the account

Additionally, you can create virtual accounts in bulk, and retrieve their details at any time. For more details, see the API reference.

If the account is created successfully, you'll get a success response. The response will have a response_code of 02 and a response_message of "Transaction in progress", indicating that the account number has been successfully created, and we're ready to accept payments for you.

    "status": "success",
    "message": "Virtual account created",
    "data": {
        "response_code": "02",
        "response_message": "Transaction in progress",
        "order_ref": "URF_1613406439309_370935",
        "account_number": "7824822527",
        "bank_name": "WEMA BANK",
        "amount": null
  "status": "error",
  "message": "BVN is required for static account number",
  "data": null

Virtual account webhooks

Next, pass the details on to your customer, and they can make a transfer into the account. When they do, we'll send you a webhook that looks like this:

  "event": "charge.completed",
  "data": {
    "id": 407347576,
    "tx_ref": "Links-221401982810",
    "flw_ref": "000016210415121239000082517439",
    "device_fingerprint": "7852b6c97d67edce50a5f1e540719e39",
    "amount": 30020,
    "currency": "NGN",
    "charged_amount": 10,
    "app_fee": 0.14,
    "merchant_fee": 0,
    "processor_response": "success",
    "auth_model": "AUTH",
    "ip": "",
    "narration": "payment for sample product",
    "status": "successful",
    "payment_type": "bank_transfer",
    "created_at": "2021-04-15T11:13:10.000Z",
    "account_id": 82913,
    "customer": {
      "id": 254967086,
      "fullname": "Flutterwave Developers",
      "phone_number": null,
      "email": "",
      "created_at": "2021-04-14T16:39:17.000Z"

if you specified a tx_ref when creating the account, the reference field will be set to that value. If you didn't set one, we'll generate a random value for that.

Now your webhook endpoint can handle the event and credit the customer's wallet. For help setting up webhooks, see our guide to webhooks.