Examples in this guide target the Sandbox (
https://api.sandbox.zuba.com).
Swap in https://api.zuba.com for production.Why Use Batch Payouts
- Efficiency: Create hundreds of payouts with a single API call, reducing API overhead
- Per-payout results: Each item in the batch is processed independently. Failures are returned inline as
{ error, clientRef }objects in the201response array, alongside the successful payout objects - Up-front validation: Request-level validation is all-or-nothing. One malformed item rejects the entire batch with a
400before anything is created
Single API Call for Multiple Payouts
The Zuba API accepts both single payout objects and arrays of payout objects at the same endpoint. 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):
senderInfo is only needed when paying out on behalf of a third party. When
omitted, your workspace is used as the business originator. Business
originators use type: "business" with companyName, registrationNumber,
and country.GET /v1/payouts/available-rails.
Preparing Beneficiaries
Before creating batch payouts, ensure all beneficiaries exist. Here’s how to bulk create them:iban accounts
(EUR/GBP) require iban and accountHolderName, with bic optional. Use
GET /v1/payouts/requirements to check the required fields for a currency, and
see Account Types for every shape.
CSV Import Example
Process payroll data from a CSV file:Monitoring Batch Progress
Webhooks are the primary mechanism for tracking payout status: subscribe once and receive an event every time a payout in the batch transitions (for example toprocessing, paid, or failed), with no polling.
If you do need to poll, prefer GET /v1/payouts (the list endpoint) over
per-ID requests. For a small batch, per-ID polling looks like this:
Error Handling in Batches
Batch creation is not atomic. There are two distinct failure modes:- Validation failure (
400): if any payout in the array fails request validation, the entire batch is rejected before anything is created.detailscontains per-field errors ({ field, message }) for the first invalid payout. - Per-payout failure (inside a
201): once validation passes, each payout is processed independently. A payout that cannot be created (for example, insufficient balance or a duplicateclientRef) is returned as an inline{ error, clientRef }element in the response array; the other payouts succeed normally.
Retries and Idempotency
clientRef is unique per workspace, which makes retries safe by construction:
- A single-payout request that reuses a
clientRefis rejected with400Payout with this client reference already exists. - In a batch, a duplicate
clientRefsurfaces as an inline{ error, clientRef }element in the201response array; the other rows are unaffected. - Re-submitting a partially failed batch is therefore safe: rows that were already created come back as duplicate errors, and only the previously failed rows create payouts.
Idempotency-Key header for
transport-level retries: a repeat with the same key and the same body replays
the original response without creating new payouts; the same key with a
different body is rejected with 422; a repeat while the original request is
still in flight returns a retryable 409. Keys expire 24 hours after the
original request completes.
Best Practices
1. Batch Size Limits
Keep batch sizes reasonable to avoid timeouts:2. Validation Before Submission
Validate all payout data before submission. The lists below cover the live destination currencies and routes; preferGET /v1/payouts/available-rails
and GET /v1/payouts/requirements to check availability dynamically:
3. Progress Reporting
Implement progress tracking for large batches:Complete Batch Payout Example
Here’s a complete implementation:Next Steps
- Set up webhooks to receive real-time status updates for your batch payouts
- Learn about error handling strategies for production batch processing
- Explore the API reference for advanced payout options