A Paystack subaccount represents a third-party recipient in a split payment. You create one by calling POST /subaccount with the recipient's business name, bank code, account number, and default percentage charge. Paystack verifies the bank details, assigns a subaccount_code, and settles the subaccount's share of every split transaction directly to their bank account.
A subaccount on Paystack is a record that represents a third party who receives a share of a split payment. It stores the party's business name, bank account details, a default split ratio, and a settlement configuration. When you initialize a transaction with a subaccount code, Paystack knows where to send that party's share of the money.
Every entity that receives money through your platform needs a subaccount. On a food delivery app, each restaurant gets a subaccount. On a freelance marketplace, each freelancer gets one. On a multi-vendor e-commerce store, each seller gets one. The subaccount is the link between a user in your system and a bank account that Paystack settles to.
Subaccounts are not user accounts. They do not have login credentials, dashboards, or permissions within Paystack. They are settlement destinations. Your vendors interact with your platform, not with Paystack directly. You manage subaccounts through your backend using the Paystack API.
Key properties of a subaccount:
ACCT_abc123xyz). You pass this when initializing split transactions.Before creating a subaccount, validate the vendor's bank details using Paystack's Resolve Account endpoint. This endpoint checks whether the account number matches a real account at the specified bank and returns the account holder's name. If the details are invalid, creation will fail, and you want to catch that before it hits the subaccount creation call.
// Step 1: Get the list of banks for the vendor's country
var banksResponse = await fetch(
'https://api.paystack.co/bank?country=nigeria',
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var banks = await banksResponse.json();
// banks.data is an array of { name, code, ... }
// Display these in a dropdown for the vendor to select their bank
// Step 2: Resolve the account number against the selected bank
var resolveResponse = await fetch(
'https://api.paystack.co/bank/resolve?account_number=0123456789&bank_code=058',
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var resolved = await resolveResponse.json();
if (resolved.status) {
console.log('Account name:', resolved.data.account_name);
// Show the account name to the vendor for confirmation
// "Is this your account? Mama Oliech Kitchen"
} else {
console.log('Invalid account details:', resolved.message);
// Ask the vendor to re-enter their details
}
This two-step validation (list banks, then resolve account) is worth implementing even if it adds an extra screen to your onboarding flow. The alternative is subaccount creation failures that confuse vendors and create support tickets. Show the resolved account name to the vendor and ask them to confirm before proceeding to subaccount creation.
Common validation failures:
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Once you have validated the bank details, create the subaccount with a single API call.
// Create a subaccount for a vendor
async function createSubaccount(vendor) {
var response = await fetch('https://api.paystack.co/subaccount', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
business_name: vendor.businessName,
settlement_bank: vendor.bankCode,
account_number: vendor.accountNumber,
percentage_charge: 80,
description: 'Vendor on ' + process.env.PLATFORM_NAME,
primary_contact_email: vendor.email,
primary_contact_phone: vendor.phone,
metadata: JSON.stringify({
vendor_id: vendor.id,
onboarded_at: new Date().toISOString(),
}),
}),
});
var data = await response.json();
if (data.status) {
// Store the subaccount code in your database
await db.vendors.update(vendor.id, {
paystackSubaccountCode: data.data.subaccount_code,
paystackSubaccountId: data.data.id,
});
return data.data;
} else {
throw new Error('Subaccount creation failed: ' + data.message);
}
}
Important things to know about the creation call:
058.Vendors change banks. They update their business names. They renegotiate their commission rate. You handle all of these with the Update Subaccount endpoint.
// Update a vendor's bank details
async function updateSubaccountBank(subaccountCode, newBankCode, newAccountNumber) {
// Validate new details first
var resolveResponse = await fetch(
'https://api.paystack.co/bank/resolve?account_number=' + newAccountNumber + '&bank_code=' + newBankCode,
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var resolved = await resolveResponse.json();
if (!resolved.status) {
throw new Error('New bank details are invalid: ' + resolved.message);
}
// Update the subaccount
var response = await fetch(
'https://api.paystack.co/subaccount/' + subaccountCode,
{
method: 'PUT',
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
settlement_bank: newBankCode,
account_number: newAccountNumber,
}),
}
);
var data = await response.json();
if (data.status) {
console.log('Subaccount updated successfully');
return data.data;
} else {
throw new Error('Update failed: ' + data.message);
}
}
What happens to pending settlements when you update bank details: transactions that were already processed and are awaiting settlement will still go to the old bank account. New transactions initiated after the update will use the new bank details. There is no way to redirect pending settlements to the new account.
You can update any of these fields:
Tip: always validate new bank details with the Resolve Account endpoint before updating. The update call will fail if the new bank details are invalid, just like creation.
As your marketplace grows, you will need to list, search, and fetch subaccounts programmatically. Paystack provides endpoints for both listing all subaccounts and fetching a single one.
// List all subaccounts with pagination
async function listSubaccounts(page, perPage) {
var url = 'https://api.paystack.co/subaccount?perPage=' + (perPage || 50) + '&page=' + (page || 1);
var response = await fetch(url, {
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
});
var data = await response.json();
// data.data is an array of subaccount objects
// data.meta contains pagination info: total, page, perPage
return data;
}
// Fetch a single subaccount by code or ID
async function getSubaccount(subaccountCode) {
var response = await fetch(
'https://api.paystack.co/subaccount/' + subaccountCode,
{
headers: {
Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY,
},
}
);
var data = await response.json();
return data.data;
}
For marketplaces with hundreds of vendors, you will probably sync subaccount data to your local database rather than querying Paystack every time. The recommended pattern is:
This approach minimizes API calls to Paystack during the critical checkout flow and keeps your vendor management fast even at scale.
By default, subaccounts are settled on Paystack's standard settlement cycle for your market. But you can configure settlement behavior per subaccount to match your business needs.
Auto settlement (default)
Paystack settles the subaccount's share automatically on the normal settlement cycle. You do not need to trigger anything. This is what most marketplaces use for straightforward vendor payouts.
Manual settlement
Paystack holds the subaccount's share until you explicitly trigger settlement. This is how you build escrow-like flows or hold-and-release patterns. The money stays with Paystack until your backend calls the settlement endpoint or you release it from the dashboard.
You set the settlement schedule when creating or updating the subaccount. The exact parameter and options depend on the Paystack API version, so check the current documentation for the right field name. The concept is consistent: you are telling Paystack whether to settle this subaccount automatically or wait for your signal.
Manual settlement is powerful but adds operational complexity. You need to build the logic for when to release funds, handle cases where the release trigger fails, and ensure you do not hold funds longer than Paystack's policies allow. Use it when your product genuinely needs a hold period (delivery confirmation, work approval) and not just because it sounds safer.
For a detailed walkthrough of delayed settlement patterns, including hold-and-release code and the edge cases around timing, see delayed settlement patterns for marketplaces.
These practices come from running subaccounts at scale in production marketplaces.
One vendor, one subaccount. Never create multiple subaccounts for the same vendor. If you need to change the split ratio for a specific transaction, override it at the transaction level using transaction_charge. If you need to change the default ratio permanently, update the existing subaccount.
Store the subaccount_code, not just the ID. You will pass the subaccount_code in every transaction initialization. Make it easy to look up from your vendor record. Index it in your database.
Handle creation failures gracefully. Subaccount creation can fail for several reasons: invalid bank details, duplicate account numbers, or API errors. Your onboarding flow should catch these, explain the problem to the vendor in plain language, and let them retry with corrected details.
Build an admin view for subaccounts. Your operations team needs to see which vendors have subaccounts, which ones have invalid or outdated details, and which ones have never received a settlement. A simple admin page that lists subaccounts with their status saves hours of dashboard digging on Paystack's side.
Monitor settlement health. Set up alerts for subaccounts that have transactions but no successful settlements. This catches problems like invalid bank details that were not caught during validation, closed bank accounts, or bank connectivity issues on Paystack's side.
Do not expose subaccount codes to the frontend. The subaccount code is an internal identifier used in your backend logic. The frontend sends the order details to your backend, and your backend looks up the right subaccount code from the vendor record. Never let the frontend dictate which subaccount receives money.
For the full context of how subaccounts fit into split payment architecture, see the Paystack split payments and marketplaces complete guide.
KES 2,999
The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.
Start Paystack Course