Skip to main content
Before you start: the OMS API is in early access. Every endpoint, including the ones in this guide, requires an early-access API key. Request access before you begin. Bulk payouts are enabled per project; contact us before you build against them.
This guide shows you how to pay many recipients, such as contractors, sellers, or payroll, from one treasury wallet. There is no batch endpoint: a payout run is one quote and one transaction per recipient, which you create in parallel and reconcile together. The treasury is an OMS wallet owned by a business customer that represents your platform. Each recipient is a counterparty of that customer with a registered bank account. Payouts are in USD over US rails (ach, achSameDay, wire, rtp) or SWIFT, or in CAD to Canadian accounts.
Per-recipient payout pattern
Platform treasury
Business customer’s OMS wallet
RecipientRailQuoteTransaction
AliceACHwlt_treasury → ext_alicetxn_alice
BobACHwlt_treasury → ext_bobtxn_bob
CarlosSWIFTwlt_treasury → ext_carlostxn_carlos

Step 1: Register each recipient once

For each new recipient:
  1. Create a counterparty with POST /counterparties: customerId (your treasury customer), entityType (individual or business), and firstName and lastName or businessName. Address and contact details are optional unless your project requires them.
  2. Register their bank account with POST /external-accounts, setting owner to { "kind": "counterparty", "counterpartyId": "ctp_..." }, type to bankUs, bankIban, or bankCanada, and the matching detail object.
  3. Wait until the account is active (the externalAccount.statusChanged webhook tells you), then store its ext_ ID against the recipient.
Pay a third party walks through these calls with full request and response bodies.

Step 2: Quote and execute each payout

For every recipient in the run, create a quote from the treasury wallet to the recipient’s account, then accept it:
  • Set amount on the destination to pay an exact amount; OMS computes the USDC to pull, fees included.
  • A quote expires after 30 seconds by default, so accept each one as soon as it is created rather than quoting the whole run first.
  • Derive each Idempotency-Key from the run and recipient, as above, so a retried request cannot pay anyone twice.
  • Quote metadata is copied onto the transaction; use it to tag the run.
  • A bankIban destination uses network: "swift"; a bankCanada destination uses swift for USD or local for CAD.

Step 3: Reconcile the run

Follow each payout with the transaction.statusChanged webhook, or list the treasury customer’s transactions with GET /transactions?customerId=cst_treasury.... Each transaction’s destination.owner identifies the counterparty that was paid. For bank references (rail, imad, uetr) fetch the payout transfer with GET /transfers/{transferId}; see Bank transfers. A payout that fails is retried by OMS where it can (processing.payoutRetrying). If it ends failed, read failure and create a new quote once the cause is fixed.