Virtual accounts
Receive, identify, and reconcile payments with unique virtual account numbers and vIBANs linked to your main bank account
A virtual account is a unique account number, such as a virtual IBAN (vIBAN), linked to a main bank account at your partner bank.
How it works
Your partner bank holds one main account. It generates and attaches multiple virtual account numbers to it. Each payer receives a different number.
When your bank reports a credit, Mambu Payments reads the virtual account number on it. Mambu Payments then links the transaction to the matching virtual account. You know which payer, invoice, or loan the funds belong to without parsing payment references.
Use virtual account numbers in reconciliation rules and expected payments to match transactions automatically. You can also originate payments from a virtual account.
In Mambu Payments, a virtual account is an internal account with type set to virtual. Manage it with the internal accounts API endpoints, the dashboard, and internal_account webhook events.
Key concepts
| Term | Definition |
|---|---|
| Connected account | The main bank account at your partner bank. Virtual accounts attach to it. |
| Internal account | An account your organization holds for a customer. It has a type of own or virtual. |
| Virtual account number | The identifier payers send funds to. It is an IBAN, a UK account number, or any alphanumeric string of up to 35 characters. |
| Account holder | The person or organization a virtual account is held for. Linking one is optional. |
| Bank synchronization | The exchange of creation and lifecycle requests with your bank. |
Choose how to provision a virtual account
Mambu Payments supports two provisioning modes.
| Mode | How it works | Initial status |
|---|---|---|
| Register an existing number | You provide a number your bank already issued. | active |
| Create at your bank | Mambu Payments requests the number from your bank. | pending_creation |
Account numbers are unique across all non-disabled internal accounts in your organization. You can reuse a number after you disable the internal account that holds it.
Register an existing virtual account
To register a number your bank already issued, create an internal account with type set to virtual.
curl --request POST \
--url https://sandbox.numeral.io/v1/internal_accounts \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api_key>' \
--data '
{
"type": "virtual",
"name": "Dupont Logistics - invoices",
"holder_name": "Dupont Logistics SAS",
"account_number": "FR7630004000031234567890143",
"connected_account_ids": ["a5d1f662-28ff-4215-9bdd-d735cdb5f8d7"],
"account_holder_id": "de785fd3-da86-45c7-a185-719ebfb2122d",
"metadata": { "customer_ref": "CUST-1042" }
}
'The response returns the internal account with status active. Mambu Payments sends an internal_account.created event.
Create a virtual account at your bank
To have your bank issue the number, set synchronized_with_bank to true. For most banks, omit account_number and let the bank allocate it.
curl --request POST \
--url https://sandbox.numeral.io/v1/internal_accounts \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api_key>' \
--data '
{
"type": "virtual",
"name": "Dupont Logistics - invoices",
"holder_name": "Dupont Logistics SAS",
"connected_account_ids": ["a5d1f662-28ff-4215-9bdd-d735cdb5f8d7"],
"synchronized_with_bank": true
}
'The response returns status pending_creation. When the bank answers, the status changes to active or creation_rejected. The final account_number arrives in an internal_account.updated event.
Wait for the internal_account.created event before you share the number with your customer.
Track the virtual account lifecycle
A virtual account has one of five statuses.
| Status | Meaning |
|---|---|
pending_creation | The bank received the creation request and has not confirmed it. |
creation_rejected | The bank refused the creation request. |
active | The virtual account receives and sends payments. |
blocked | The virtual account is suspended. You can unblock it. |
disabled | The virtual account is closed. You cannot enable it again. |
Only bank-synchronized accounts use pending_creation and creation_rejected. The bank's answer sets them. You cannot set them yourself.
Use these endpoints to manage an existing virtual account.
| Action | Endpoint | Requirement |
|---|---|---|
| Update | POST /internal_accounts/<internal_account_id> | None |
| Block | POST /internal_accounts/<internal_account_id>/block | Status is active. |
| Unblock | POST /internal_accounts/<internal_account_id>/unblock | Status is blocked. |
| Disable | POST /internal_accounts/<internal_account_id>/disable | Status is not disabled. |
Update names, holder details, addresses, currencies, metadata, and custom fields. You cannot change the account number, type, or connected accounts.
Block and disable accept a reason. The values are fraud, duplicate, account_holder_deceased, account_holder_offboarded, and other. The default is other.
Caution: Disabling a virtual account is irreversible. Block the account instead to suspend it temporarily.
Disabling an account holder disables all of its internal accounts. For bank-synchronized accounts, Mambu Payments forwards updates, blocks, unblocks, and disables to your bank if the bank supports them.
Listen to virtual account events
Subscribe to the internal_account webhook topic. Each event carries the full internal account object.
| Event | Sent when |
|---|---|
internal_account.pending_creation | You create a bank-synchronized account. |
internal_account.created | The account becomes active. |
internal_account.creation_rejected | The bank refuses the creation request. |
internal_account.updated | Any attribute changes, including the number returned by the bank. |
internal_account.blocked | You block the account. |
internal_account.unblocked | You unblock the account. |
internal_account.disabled | You disable the account. |
A bank-synchronized account typically produces a pending_creation event, then one or more updated events, then a createdevent.
Receive payments on a virtual account
When your bank reports a credit to a virtual account number, Mambu Payments follows three steps.
- Store the number on the transaction.
- Search for an internal account of type
virtualwith that number on the transaction's connected account. - Set
internal_account_idon the transaction if exactly one account matches.
The transaction then references the virtual account.
{
"id": "b6c9d3a2-8f41-4e7b-9a15-2c0e5f7d1b38",
"object": "transaction",
"internal_account_id": "0d3b9d33-21d4-47c5-9421-be899ed8a4ba",
"internal_account": {
"account_number": "FR7630004000031234567890143",
"holder_name": "Dupont Logistics SAS"
}
}If no virtual account matches, Mambu Payments still records the transaction with the virtual account number. In that case, internal_account_id is empty.
To retrieve the transactions of one virtual account, filter List transactions by internal_account_id or internal_account_number.
Reconcile payments with virtual accounts
Virtual accounts make reconciliation deterministic.
- Create one virtual account for each invoice, customer, or loan.
- Create an expected payment and set
internal_account_idto that virtual account. - Create a reconciliation rule that matches on the virtual account number,
amount,direction, and date.
Mambu Payments then reconciles a matching transaction automatically, whatever reference the payer wrote. See Understanding reconciliations.
Send payments from a virtual account
To originate a payment from a virtual account, set originating_account_id on the payment order.
curl --request POST \
--url https://sandbox.numeral.io/v1/payment_orders \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api_key>' \
--data '
{
"connected_account_id": "a5d1f662-28ff-4215-9bdd-d735cdb5f8d7",
"type": "sepa",
"direction": "credit",
"amount": 125000,
"currency": "EUR",
"originating_account_id": "0d3b9d33-21d4-47c5-9421-be899ed8a4ba",
"receiving_account": {
"account_number": "DE89370400440532013000",
"holder_name": "Schneider GmbH"
}
}
'Mambu Payments uses the virtual account number and holder details as the originating account. The payment order must meet these conditions:
- The virtual account has status
active. - The virtual account is attached to the
connected_account_idof the payment order. - For SEPA direct debits, the virtual account has a
creditor_identifier.
In the dashboard, turn on Use virtual account when you create a payment order on a corporate connected account. See Create a payment order.
Check bank support
Virtual account support depends on the bank behind your connected account.
| Capability | Description |
|---|---|
| Incoming payment attribution | Mambu Payments links credits to your virtual accounts when your bank reports the virtual account number in its statements or notifications. |
| Creation at the bank | Mambu Payments creates virtual accounts on your bank's side through its API. Set synchronized_with_bank to true. |
| Update, block, and disable at the bank | Mambu Payments forwards these actions to your bank if the bank supports them. |
| Sending payments | Not all banks support sending payments using virtual accounts. |
If your bank does not support creation through its API, create the virtual accounts at the bank and register them in Mambu Payments.
Limits and constraints
Required permissions:
counterparties.view_internal_accountsto view virtual accounts.counterparties.create_internal_accountsto create them.counterparties.edit_internal_accountsto update, block, unblock, and disable them.
API keys need the Write right to create and the Modify right to update, block, unblock, and disable.
Related
Updated about 1 hour ago
