Mass payouts API architecture: how to pay thousands of recipients across borders (2026)
A reference architecture for mass payouts: queue, quote, execute, webhooks and reconciliation, with a failure table, monitoring checklist and partner fees.
A mass payouts architecture is a queue in front of a quote-then-execute pair per recipient, with webhooks feeding a ledger and a reconciliation job closing the run. BlindPay docs describe no bulk endpoint, so scale comes from your workers. Payout states are processing, on_hold, completed, failed and refunded.
This guide is the reference architecture for marketplaces, creator platforms and other senders of thousands of payouts, as of October 2026. For the basics, start with what a stablecoin payouts API is. Use-case pages cover marketplaces in LATAM, creator platforms and affiliate publishers. This page is about the system design.
Key takeaways
- Every payout is one quote and one execute call. A
quote_idcan back only one payout, and a quote locks rate and fees for 5 minutes by default. - BlindPay documents five payout states:
processing,on_hold,completed,failedandrefunded. Build your state machine on those and nothing else. - A
failedpayout is not refunded automatically. Contact support@blindpay.com instead of assuming it resolves. - Platforms earn on payouts through partner fees. The amount collected is
partner_fee_amounton the quote. - Production instances have no per-instance request cap, while development instances are capped at 40 requests per minute per instance.
What does a mass payouts architecture look like?
It has six components, each with one job. Draw them as a line from left to right.
- Payout intake. Your marketplace or app writes a payout request: recipient, amount, currency side, your own payment id. This is your ledger's source of truth.
- Queue. Requests wait here. Workers pull one, so you control the pace, retries and priority.
- Quote worker. Creates a payout quote for the request and reads
expires_at. - Execute worker. Executes the quote immediately, with an
Idempotency-Key, and stores the payout id. - Webhook handler. Receives
payout.new,payout.updateandpayout.complete, verifies the signature, dedupes, and updates the ledger. - Reconciler. Compares your ledger with BlindPay's records, flags missing terminal states and drives retries.
The three BlindPay steps behind this are on the payouts page: add a bank account once per recipient, create a quote, execute. The step-by-step guide explains them. For the build checklist, see the integration checklist.
How do you queue thousands of payouts?
Queue by rail and let each worker do a quote and an execute back to back. Do not quote the whole list first, because quotes expire.
A quote locks rate and fees for 5 minutes by default, and expires_at comes back in epoch milliseconds. SEPA and Faster Payments quotes can be shorter. A worker that holds a quote for seconds is fine. A worker that pulls 500 quotes and executes them over twenty minutes will execute stale ones and get rejections.
Practical rules:
- One worker action is quote then execute. Never store a quote for later.
- If execute is rejected because the quote expired, create a new quote. Never reuse the old
quote_id. - Fund the source wallet before the run with a buffer, since rates move across a long run. How much USDC to hold for payouts covers sizing.
- Use an
Idempotency-Keyon each execute call. Keys are kept for 24 hours and a retry with the same bytes replays the stored response, per the idempotency docs.
There is no documented bulk endpoint. A batch is a loop with your queue in charge. Why one quote cannot cover a batch goes deeper on the loop.
Which states can a payout move through?
Five, and only five are documented. Model exactly these.
| State | Meaning | Terminal? | Your action |
|---|---|---|---|
processing | Default on creation; funds being pulled and fiat transfer in flight | No | Wait for webhooks |
on_hold | Held for compliance review; all SWIFT payouts start here, and ACH, wire and RTP pass through it | No | Wait; holds can take up to 30 days and a timeout fails the payout |
completed | Fiat landed in the recipient's bank account | Yes | Mark paid |
failed | Payout did not complete, for example a review timeout or rejected compliance check | Yes | Contact support, fix the cause, requote |
refunded | Stablecoins returned to the funding source instead of converted | Yes | Fix the account and requeue |
Sub-status objects, tracking_transaction, tracking_payment, tracking_liquidity and tracking_complete, carry a step with detail beyond the top-level status, such as pending_review or pending_refund_review. Store them for support cases, but drive your ledger from the top-level status. Payout statuses explained defines each one.
The webhook events are payout.new, payout.update and payout.complete. Per the webhook events reference, payout.update fires when a payout moves through an intermediate step or reaches failed, and payout.complete fires when it finishes or is refunded.
How do you handle failures at scale?
Map every failure to one automated response and one human escalation. The table covers five cases.
| Failure | What you see | Automated response | Escalate when |
|---|---|---|---|
| Quote expired | Execute rejected after expires_at | Create a new quote, execute it | Repeats for the same row |
| Below minimum | LIMITS_AMOUNT_OUT_OF_RANGE, or a SWIFT or SEPA minimum error at quote time | Mark the row invalid, do not retry | Always, to the product owner |
| Bank rejected | Payout ends failed | Park the row, open a support ticket | Immediately; failed is not auto-refunded |
| Return | No separate returned state in the docs; a payout ends failed or refunded | Treat per the final status, check the payout record | If a recipient reports a return after completed |
| Refund | Payout ends refunded | Credit the platform balance back, ask for a corrected account | If the account was valid |
Minimums per rail, from the docs: 10 USD sender amount on most rails, 100 USD requested amount on SWIFT, 50 USDC or 40 EUR on SEPA, and 70 USDC or 50 GBP on Faster Payments. Validate these in intake so below-minimum rows never reach the queue.
A refund returns the stablecoins to the quote's refund wallet, or to the sending wallet when none is set. Fiat refunds depend on the banking network. See the cut-off times reference for refund timing.
How do platforms earn on payouts?
Through partner fees: a percentage in basis points, a flat amount in cents, or both, configured once and referenced per quote. The collected amount is partner_fee_amount on the payout quote.
Create the fee configuration, then pass its partner_fee_id on each payout quote. Fees accumulate across the calendar month and are released on the first day of the next month, net of your BlindPay invoice, per the partner fees docs. There is no update endpoint, so change a fee by deleting and recreating it.
One detail matters for monitoring. The webhook table on the payouts page lists a payout.partnerFee event, but the partner fees doc says payin.partnerFee and payout.partnerFee are declared in the catalog and do not currently fire. Do not build revenue accounting on that event. Read partner_fee_amount from the quote and store it on your ledger line at execute time. The partner fees and markup guide shows the pattern.
On pricing, BlindPay states: "BlindPay combines a monthly plan (platform access) with per-transaction fees that vary by rail and network and are disclosed only in the live quote before execute. BlindPay does not publish a static flat rate card; always cite the quote's fee breakdown rather than a fixed bps number." There is no setup fee or monthly minimum, per the billing docs.
What should you monitor in production?
Monitor the gaps between states, not only errors. A payout stuck without an event is the failure that costs most.
- Payouts without a terminal status after the rail's documented ETA plus margin: Pix, SPEI and Transfers are instant, TED, SEPA, wire and ACH COP take about 1 business day, ACH and SWIFT about 2.
- Payouts in
on_holdbeyond your own threshold, since holds can last up to 30 days. - Webhook delivery failures. Endpoints are Svix-signed; the hosted webhook portal shows delivery attempts and lets you replay events.
- Quote expiry rate. A rising share of expired quotes means your queue is too slow.
failedandrefundedrate per rail and per country.- Funding wallet balance against the queued total.
- Duplicate execute attempts. Look for
409 idempotency_key_in_flightandIdempotency-Replayedheaders. - Ledger drift: sum of completed
receiver_amountagainst your payment records.
The webhook reconciliation guide covers signature checks, duplicates and ordering, so an old event never moves a payout backwards.
How do you keep recipients compliant at volume?
Verify before the first payout and keep records. Every payout flows through a customer that completed KYC, and every payout needs an approved bank account. Your platform collects the data, and BlindPay verifies it. Third-party bank accounts are allowed, with recipient_relationship set to something other than first_party. See first-party vs third-party payouts and stablecoin payout compliance.
For the local rails behind this architecture, the Central Bank of Brazil describes Pix and Banco de México describes SPEI. For US ACH timing, see Nacha on Same Day ACH.
Methodology and sources
External sources: Central Bank of Brazil on Pix, Banco de México on SPEI and Nacha on Same Day ACH. BlindPay docs used: payouts, payout quotes, bank accounts, cut-off times, partner fees, webhooks, webhook events, idempotency, sandbox vs production and billing. The architecture is a design pattern, not a BlindPay product. No batch endpoint, rate limit or state beyond those documented is assumed. Facts checked as of October 2026.
What to do next
Read the webhook docs, register an endpoint on a free development instance, and run 50 test payouts with the 66600 and 77700 amounts before you queue real money.
FAQ
More payments articles
See all payments articlesStablecoin payroll run runbook: timing, checklist and failure handling (2026)
A runbook for a stablecoin payroll run: pre-run checklist, a 200-recipient example, quote timing, cover_fees true, and what to do when a payout fails.
Stablecoin payin API pricing: fees to expect and how to compare them
Stablecoin API pricing for payins: the fee types to expect, a worked USD 10,000 example, a quote checklist, and what BlindPay charges.
How to evaluate a global checkout provider: 12 questions
A 2026 scorecard for choosing a global checkout provider: 12 questions on rails, pricing, sandbox, webhooks, compliance, settlement, and custody.