Bonaventure OgetoBy Bonaventure Ogeto|

Subaccounts on Paystack: Complete Guide

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.

What a Subaccount Is

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:

  • subaccount_code: A unique identifier assigned by Paystack (like ACCT_abc123xyz). You pass this when initializing split transactions.
  • business_name: The name of the vendor or partner. Paystack uses this for identification in the dashboard and settlement reports.
  • settlement_bank and account_number: The bank details where Paystack settles the subaccount's share.
  • percentage_charge: The default percentage of each transaction that goes to the subaccount. Can be overridden per transaction.

Validating Bank Details Before Creation

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:

  • Wrong bank code: The vendor selected the wrong bank. Double-check with the list from the List Banks endpoint.
  • Account number length: Nigerian account numbers are 10 digits. Kenyan account numbers vary by bank. Make sure your input field enforces the correct length.
  • Account does not exist: The number is valid in format but does not correspond to a real account. Ask the vendor to double-check.

Creating a Subaccount

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:

  • percentage_charge is required. Set it to the default percentage the subaccount should receive. For an 80/20 split where the vendor gets 80%, set this to 80. You can always override it per transaction.
  • settlement_bank uses bank codes, not bank names. Use the codes from the List Banks endpoint. For example, GTBank Nigeria is 058.
  • Paystack verifies the bank details during creation. If the account number does not match the bank, the call will fail. This is why you validate first with Resolve Account.
  • metadata is optional but valuable. Store your internal vendor ID in the metadata so you can trace subaccounts back to your system when reviewing them in the Paystack dashboard.
  • primary_contact_email and primary_contact_phone are optional but recommended. Paystack may use these to contact the subaccount holder about settlement issues.

Updating a Subaccount

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:

  • business_name: If the vendor rebrands or corrects a typo
  • settlement_bank and account_number: When the vendor changes banks
  • percentage_charge: When you renegotiate the commission rate
  • description, primary_contact_email, primary_contact_phone: For metadata updates

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.

Listing and Fetching Subaccounts

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:

  1. Create or update the subaccount on Paystack when the vendor onboards or changes details
  2. Store the subaccount_code and key details in your local vendors table
  3. Use your local database for lookups during transaction initialization
  4. Periodically reconcile your local records against Paystack's list to catch any drift

This approach minimizes API calls to Paystack during the critical checkout flow and keeps your vendor management fast even at scale.

Settlement Configuration on Subaccounts

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.

Production Best Practices

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.

Build Marketplace Payment Systems

Subaccounts are the foundation of every marketplace built on Paystack. Getting them right means getting the vendor experience right, and that is half the battle in marketplace building.

If you want to learn how to build production payment systems from scratch, the McTaba bootcamp covers Paystack integration, marketplace architecture, and deployment as core modules.

Create a free McTaba account

Key Takeaways

  • A subaccount stores a third party's bank details and default split configuration. Every vendor, seller, or partner in your marketplace gets one subaccount.
  • Always validate bank details using the Resolve Account endpoint before creating a subaccount. This catches mismatched account numbers and bank codes before they cause creation failures.
  • The subaccount_code is your primary identifier. Store it in your database alongside the vendor record and pass it when initializing split transactions.
  • percentage_charge sets the default split ratio. If set to 80, the subaccount gets 80% and your main account gets 20%. You can override this per transaction.
  • Subaccounts can be updated. When a vendor changes banks, PUT to /subaccount/{id_or_code} with new bank details. New transactions use the updated details immediately.
  • Paystack settles subaccounts directly. The money never passes through your main Paystack balance. This is the fundamental difference between splits and manual transfers.

Frequently Asked Questions

Can a vendor have multiple subaccounts on my platform?
Technically you can create multiple subaccounts for the same vendor, but it is not recommended. Each subaccount maps to a bank account, and having multiple creates confusion about which one to use for transactions. If the vendor needs to receive payments to different accounts for different purposes, consider using one subaccount and handling the distribution on your side with transfers.
What happens to a subaccount when I deactivate a vendor?
Paystack does not have a concept of deactivating a subaccount. If you deactivate a vendor on your platform, stop including their subaccount code in new transactions. Any pending settlements for already-completed transactions will still be processed. If you need to prevent future splits to a deactivated vendor, enforce that in your application logic rather than relying on Paystack.
Can I delete a subaccount on Paystack?
Paystack does not provide a delete endpoint for subaccounts. Once created, a subaccount persists. If a vendor leaves your platform, stop using their subaccount code for new transactions. The subaccount will remain in your Paystack dashboard but will not receive any new settlements if no transactions reference it.
Do I need to create subaccounts in test mode and again in live mode?
Yes. Test mode and live mode are completely separate environments on Paystack. Subaccounts created with your test secret key only exist in test mode. When you go live, you need to create all subaccounts again using your live secret key. Build your onboarding flow to handle both modes so the transition is smooth.
Can a subaccount have a different currency than my main account?
No. Subaccounts must be in the same country and currency as your Paystack business registration. If your business is in Nigeria, subaccounts must use Nigerian bank accounts and settle in NGN. For cross-currency needs, you would handle the conversion and cross-border transfer outside of the split mechanism.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon