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.
ach, achSameDay, wire, rtp) or SWIFT, or in CAD to Canadian accounts.
Step 1: Register each recipient once
For each new recipient:- Create a counterparty with
POST /counterparties:customerId(your treasury customer),entityType(individualorbusiness), andfirstNameandlastNameorbusinessName. Address and contact details are optional unless your project requires them. - Register their bank account with
POST /external-accounts, settingownerto{ "kind": "counterparty", "counterpartyId": "ctp_..." },typetobankUs,bankIban, orbankCanada, and the matching detail object. - Wait until the account is
active(theexternalAccount.statusChangedwebhook tells you), then store itsext_ID against the recipient.
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
amounton 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-Keyfrom the run and recipient, as above, so a retried request cannot pay anyone twice. - Quote
metadatais copied onto the transaction; use it to tag the run. - A
bankIbandestination usesnetwork: "swift"; abankCanadadestination usesswiftfor USD orlocalfor CAD.
Step 3: Reconcile the run
Follow each payout with thetransaction.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.