Skip to main content

Overview

Payouts are the core functionality of the Zuba platform, enabling you to send money to beneficiaries worldwide through various payment rails including SEPA, SWIFT, crypto networks, and local payment methods.

Payout Lifecycle

Beneficiaries & Accounts

Beneficiary Management

Before sending payouts, you must create beneficiaries with their payment details:
const beneficiary = await fetch(`https://api.zuba.com/v1/beneficiaries/${beneficiaryId}`, {
  method: 'GET',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});
const beneficiary = await fetch('https://api.zuba.com/v1/beneficiaries', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'John Doe',
    email: 'john.doe@example.com',
    country: 'US',
    countrySubdivision: 'NY',
    address: '123 Main St',
    city: 'New York',
    postcode: '10001',
    accounts: [{
      type: 'bank_account',
      currency: 'USD',
      data: {
        accountNumber: '1234567890',
        routingNumber: '021000021',
        accountHolderName: 'John Doe',
        bankName: 'Chase Bank'
      }
    }]
  })
});

Account Types

Zuba supports multiple account types for maximum flexibility:
SEPA (European Union)
{
  "type": "iban",
  "currency": "EUR",
  "data": {
    "iban": "DE89370400440532013000",
    "accountHolderName": "John Doe",
    "bic": "DEUTDEFF"
  }
}
US ACH
{
  "type": "bank_account",
  "currency": "USD",
  "data": {
    "accountNumber": "1234567890",
    "routingNumber": "021000021",
    "accountHolderName": "John Doe",
    "bankName": "Chase Bank"
  }
}
UK Bank Account
{
  "type": "bank_account",
  "currency": "GBP",
  "data": {
    "accountNumber": "12345678",
    "sortCode": "123456",
    "accountHolderName": "John Smith",
    "bankName": "Barclays"
  }
}
Nigerian Bank Account
{
  "type": "bank_account",
  "currency": "NGN",
  "data": {
    "bankCode": "044",
    "crAccount": "1234567890",
  }
}
Ghanaian Bank Account
{
  "type": "bank_account",
  "currency": "GHS",
  "data": {
    "bankCode": "gh_0002",
    "crAccount": "12223444555"
  }
}
Sandbox-only currencies
ZMW (Zambian kwacha), MZN (Mozambican metical), MWK (Malawian kwacha) and EGP (Egyptian pound) are available in the sandbox only. Payouts to them settle with simulated outcomes — see Sandbox testing. In production these currencies are not yet available and a payout to one is rejected at creation.
Zambian Bank Account (sandbox only)
{
  "type": "bank_account",
  "currency": "ZMW",
  "data": {
    "bankCode": "260001",
    "accountNumber": "1234567890",
    "accountHolderName": "Chanda Mwansa"
  }
}
The same shape applies for MZN, MWK, and EGP — any bankCode is accepted in the sandbox.

Creating Payouts

Single Payout

After creating a beneficiary, use their ID to create payouts (recommended approach). Currency Fields: inputCurrency is the currency from your account you’ll be paying from, while currency is what the beneficiary will receive (automatic conversion if different):
const payout = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    clientRef: 'PAYOUT-INV-001',
    amount: '1000.00',
    inputCurrency: 'EUR',
    currency: 'NGN',
    route: 'bank_transfer',
    senderInfo: {
      firstName: 'Jack',
      lastName: 'Jones',
      address: '456 London Road',
      city: 'London',
      postalCode: 'SW1A 1AA',
      country: 'GB',
      dateOfBirth: '1985-06-15'
    },
    beneficiary: {
      id: 'cc82fa1d-fc7a-478c-a734-d3bce40464e7'
    },
    reference: 'Invoice #INV-001',
    description: 'Payment for marketing services'
  })
});

Batch Payouts

Process multiple payouts in a single API call for efficiency:
const payouts = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify([
    {
      clientRef: 'PAYOUT-BATCH-001',
      beneficiary: { id: 'ben_1234567890' },
      amount: '1000.00',
      inputCurrency: 'EUR',
      currency: 'EUR',
      senderInfo: {
        firstName: 'Jack',
        lastName: 'Jones',
        address: '456 London Road',
        city: 'London',
        postalCode: 'SW1A 1AA',
        country: 'GB',
        dateOfBirth: '1985-06-15'
      },
      reference: 'Salary March 2024'
    },
    {
      clientRef: 'PAYOUT-BATCH-002',
      beneficiary: { id: 'ben_0987654321' },
      amount: '1500.00',
      inputCurrency: 'EUR',
      currency: 'USD',
      senderInfo: {
        firstName: 'Jack',
        lastName: 'Jones',
        address: '456 London Road',
        city: 'London',
        postalCode: 'SW1A 1AA',
        country: 'GB',
        dateOfBirth: '1985-06-15'
      },
      reference: 'Freelancer payment'
    }
  ])
});

Payment Routes

Zuba automatically selects the optimal payment route, but you can specify preferences:
RouteRegionsSpeed
sepa_creditEuropean Union1-2 business days
sepa_instEU (SEPA Instant banks)< 10 seconds
bank_transferMultiple regionsVaries by destination
achUnited States1-3 business days
fedwireUnited States (domestic wire)Same business day
swiftInternational (availability varies by destination country)1-3 business days
cryptoOn-chain (Ethereum, Solana, Tron)Minutes (network-dependent)
mobile_moneyWest Africa (XOF — CI, SN, ML, BF, BJ, TG), Central Africa (XAF — Cameroon), and Ghana (GHS)Minutes

Crypto rail

Set route: "crypto" to send a stablecoin (USDC or USDT) on-chain to a crypto wallet beneficiary. The currency is the stablecoin and is debited from your same-currency balance (no FX). The destination network and address come from the beneficiary’s wallet account (see Crypto Wallets above), so the request only references the beneficiary by id:
Crypto Payout
const payout = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    clientRef: 'PAYOUT-CRYPTO-001',
    amount: '250.00',
    currency: 'USDC',
    route: 'crypto',
    beneficiary: { id: 'cc82fa1d-fc7a-478c-a734-d3bce40464e7' },
    reference: 'Contractor payment'
  })
});
The funds are held the moment the payout is created, screened for compliance, then signed and broadcast on-chain. The payout reaches paid only once the transaction has broadcast; if it is declined or signing fails, the held funds are returned to your balance automatically. Crypto payouts are availability-gated per environment — contact your account manager to enable them. Once the transfer is broadcast, GET /v1/payouts/{id} returns the on-chain transaction hash in the txHash field (null for non-crypto payouts or before broadcast). Use it to look the transfer up on a block explorer for the payout’s network.

Mobile money (XOF, XAF, and GHS)

Set route: "mobile_money" to pay out XOF to a recipient’s mobile money wallet across West Africa, XAF to a wallet in Cameroon, or GHS to a wallet in Ghana. The destination provider and phone number come from the beneficiary’s mobile account (see Mobile Money under Account Types above):
Mobile Money Payout
const payout = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    clientRef: 'PAYOUT-MM-001',
    amount: '5000',
    currency: 'XOF',
    route: 'mobile_money',
    beneficiary: { id: 'cc82fa1d-fc7a-478c-a734-d3bce40464e7' },
    reference: 'Supplier payment'
  })
});
Supported in Côte d’Ivoire, Senegal, Mali, Burkina Faso, Benin, and Togo (XOF), Cameroon (XAF), and Ghana (GHS). The per-transaction amount depends on the destination currency — XOF 200–2,000,000, XAF 500–1,000,000, GHS 5–25,000. A mismatched provider/country, an invalid phone number, an unsupported country, a currency that isn’t XOF/XAF/GHS, or an out-of-range amount is rejected at creation. Ghana (GHS) is dual-rail. The same currency also pays out over a bank account (route: "bank_transfer"); the rail is chosen by the beneficiary’s account type (mobile vs bank_account). GHS mobile requires the sender’s first and last name via senderInfo (no phone — unlike Cameroon), or the payout is rejected:
GHS Mobile Money Payout (Ghana)
const payout = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    clientRef: 'PAYOUT-MM-GHS-001',
    amount: '100',
    currency: 'GHS',
    route: 'mobile_money',
    beneficiary: { id: 'cc82fa1d-fc7a-478c-a734-d3bce40464e7' },
    senderInfo: {
      type: 'individual',
      firstName: 'Ama',
      lastName: 'Owusu'
    },
    reference: 'Supplier payment'
  })
});
Cameroon (XAF) additionally requires sender details. Supply the originator’s name and phone in senderInfo, or the payout is rejected:
XAF Mobile Money Payout (Cameroon)
const payout = await fetch('https://api.zuba.com/v1/payouts', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    clientRef: 'PAYOUT-MM-XAF-001',
    amount: '2000',
    currency: 'XAF',
    route: 'mobile_money',
    beneficiary: { id: 'cc82fa1d-fc7a-478c-a734-d3bce40464e7' },
    senderInfo: {
      type: 'individual',
      firstName: 'Jane',
      lastName: 'Doe',
      phoneNumber: '+237655555555'
    },
    reference: 'Supplier payment'
  })
});
Mobile money is a gated corridor — contact your account manager to enable it.

USD rails

For USD payouts, three rails are surfaced by GET /payouts/available-rails?accountId=<uuid>:
  • fedwire — domestic wire to a US bank. Requires routing + account number on the beneficiary.
  • ach — batched same-day-to-3-day transfer to a US bank. Same required fields as fedwire.
  • swift — international wire via SWIFT. Requires BIC + IBAN/account number + the beneficiary’s bank country. Destination coverage spans most major markets and expands as corridors are enabled, so it is not a fixed list: GET /payouts/available-rails?accountId=<uuid> reports, per saved account, whether SWIFT currently serves that account’s destination country (available plus an unavailableReason when it does not).
The rail is fixed at beneficiary-account creation time via the route field stored on the account, so payouts against an existing account don’t re-prompt the sender to pick a method. New beneficiary accounts intended for SWIFT must be created with type: "swift" and supply swiftCode, accountNumber, beneficiaryCountry, and (optionally) bankName / beneficiaryAddress in data. A SWIFT payout from a non-USD balance (e.g. NGN) is a cross-rate send: it passes through a quote (POST /v1/quotes), is debited in the source currency, and Zuba satisfies the USD outflow on the destination bank. A same-currency USD to USD SWIFT needs no quote and is sent 1:1 from your USD balance.

Route Selection Logic

Tracking & Status

Payout Statuses

StatusDescriptionNext Actions
createdPayout has been createdCan be cancelled
queuedQueued for processingMonitor for updates
processingBeing processed by payment providerMonitor for updates
paidSuccessfully delivered to beneficiaryDownload receipt
failedProcessing failedReview error, retry
cancelledCancelled by user or systemFunds returned
A receipt can be downloaded for a payout in any status except cancelled. For a payout that is still in progress, the receipt reflects its current status.

Amounts above corridor limits

Every corridor has an amount cap, and a payout or payout quote above it is rejected by the standard bounds checks. Amounts above corridor limits are served by the orders channel (POST /v1/orders) for order-enabled accounts: a quote-first flow where funds are held from your balance and the trade is executed by Zuba’s desk within a stated execution window. See the Trade Desk Orders guide or contact us to enable orders on your account.

Real-time Tracking

const payout = await fetch(`https://api.zuba.com/v1/payouts/${payoutId}`, {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY'
  }
});

const data = await payout.json();
console.log(data.status); // created, queued, processing, paid, failed, cancelled
curl -X GET "https://api.zuba.com/v1/payouts/pay_1234567890" \
  -H "Authorization: Bearer YOUR_API_KEY"

Best Practices

  • Always validate beneficiary details before creating
  • Use SEPA reachability check for EU transactions
  • Store beneficiary IDs for repeat payments
  • Keep beneficiary information up to date
  • Implement proper error handling for all API calls
  • Use exponential backoff for retries
  • Monitor webhook notifications for status updates
  • Log all transactions for audit purposes
  • Never expose API keys in client-side code
  • Validate webhook signatures
  • Use HTTPS for all API communications
  • Implement proper access controls

Next Steps

First Payout Guide

Step-by-step guide to send your first payout

Batch Processing

Learn how to process multiple payouts efficiently

Webhooks

Set up real-time notifications for payout status

API Reference

Complete API documentation for payouts