Dynamic virtual accounts help you collect payments by generating account numbers for specific payment flows. They are useful for checkout payments, invoices, wallet top-ups, school fees, donations, rent, and other transfer-based collections.
The main service type is:
ADMIN_CREATE_DYNAMIC_COLLECTION_ACCOUNT
Use DYNAMIC_COLLECTION_ACCOUNT_TSQ to check payment status.
Dynamic account types
Type | Best for | Amount rule |
Flexible | Installments or partial payments | Set target amount and |
Non-flexible | One-time full payment | Set target amount and |
Pass-through | Open-ended collections, wallet top-ups, donations | Set |
Amounts are in kobo.
Create a flexible account
Use flexible accounts when the payer can make multiple payments until the target amount is reached.
{
"serviceType": "ADMIN_CREATE_DYNAMIC_COLLECTION_ACCOUNT",
"requestRef": "DVA20260603001",
"data": {
"amount": 5000000,
"isFlexiblePayment": true,
"accountName": "School fees",
"remittingAccounts": [
{
"splitPercentage": 100,
"accountNumber": "2000000000"
}
]
}
}
Create a non-flexible account
Use non-flexible accounts when the payer must send the full amount once.
{
"serviceType": "ADMIN_CREATE_DYNAMIC_COLLECTION_ACCOUNT",
"requestRef": "DVA20260603002",
"data": {
"amount": 12500000,
"isFlexiblePayment": false,
"accountName": "Invoice 10045",
"remittingAccounts": [
{
"splitPercentage": 100,
"accountNumber": "2000000000"
}
]
}
}
Create a pass-through account
Use pass-through accounts when there is no fixed expected amount.
{
"serviceType": "ADMIN_CREATE_DYNAMIC_COLLECTION_ACCOUNT",
"requestRef": "DVA20260603003",
"data": {
"amount": 0,
"isPassThrough": true,
"accountName": "Wallet top-up"
}
}
For pass-through accounts, accountName can be omitted. If omitted, Kuda may default the account name to the business/API profile name.
Split payments
Use remittingAccounts when incoming payments should be split across accounts.
{
"remittingAccounts": [
{
"splitPercentage": 70,
"accountNumber": "2000000000"
},
{
"splitPercentage": 30,
"accountNumber": "2000000001"
}
]
}
Make sure split percentages add up to 100.
Response
A successful response includes the dynamic account number and account name.
{
"message": "Request successful.",
"status": true,
"data": {
"accountNumber": "3020000000",
"accountName": "Invoice 10045"
}
}
Show the returned account number to the payer with clear payment instructions.
Check payment status
Use DYNAMIC_COLLECTION_ACCOUNT_TSQ.
Send the original creation requestRef, the returned accountNumber, or both. Sending both makes reconciliation easier.
{
"serviceType": "DYNAMIC_COLLECTION_ACCOUNT_TSQ",
"requestRef": "DYTSQ20260603001",
"data": {
"accountCreationRequestRef": "DVA20260603002",
"accountNumber": "3020000000"
}
}
Important account-number behavior
Dynamic account numbers can be reused under some conditions. This can happen when expired account numbers return to a business-specific pool.
To avoid reconciliation issues:
track the creation
requestReftrack the returned
accountNumbertrack the customer, invoice, order, or payment purpose in your own system
use webhooks and TSQ together for final payment confirmation
design your payment matching logic around references and account state, not account number alone
Recommended checkout flow
Customer starts checkout.
Your backend creates a dynamic account.
Your app shows the account number and amount.
Customer pays by transfer.
Your webhook endpoint stores incoming transaction notifications.
Your backend checks
DYNAMIC_COLLECTION_ACCOUNT_TSQwhen final confirmation is needed.Your app marks the order paid only after confirmed payment.
Common mistakes
Creating dynamic accounts from frontend code.
Not storing the creation
requestRef.Matching payment only by account number.
Treating account creation as payment confirmation.
Not handling partial payments for flexible accounts.
Not using TSQ when webhook delivery is delayed.