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.Authenticate by exchanging your API key and secret for a bearer token at
POST /auth/token, then send it as Authorization: Bearer {accessToken} on every request. Every POST and PATCH also requires an Idempotency-Key header. See Get started for the full flow.Prerequisites
- A customer with a
cst_ID and theusdendorsement: the payout is funded from this customer’s wallet. - A funded OMS wallet with a
wlt_ID holding enough USDC to cover the payout. - The recipient’s details: their legal name, and their bank account information.
Step 1: Create the counterparty
POST /counterparties requires the owning customerId, an entityType discriminator, and the name fields for that type:
entityType: "individual"requiresfirstNameandlastName, with optionaldateOfBirth.entityType: "business"requiresbusinessName.
email, phone, taxId, nationality, and an address.
The request is strictly validated: a missing entityType returns 422 with code: entityTypeRequired, an unrecognized value returns 422 with code: entityTypeInvalid, and a flat name field is rejected with 400. Each entityType arm rejects unknown keys, so firstName on a business request (or businessName on an individual request) returns 422 validationError.
Request
address shape is streetAddress, city, postalCode, and country (required when an address is supplied), plus an optional countryArea for the state or region.
Response, 201 Created
Responses echo the split name fields and add a read-only name, server-composed as businessName for a business or firstName + " " + lastName for an individual. The composed value is at most 140 characters.
individual counterparty, the response carries firstName, lastName, and optional dateOfBirth in place of businessName, and name renders as "{firstName} {lastName}".
Store the ctp_ ID. A counterparty that fails compliance screening comes back rejected with a rejectionReason; only active counterparties can own external accounts.
Step 2: Register the counterparty’s bank account
Register the recipient’s bank withPOST /external-accounts, setting the owner to the counterparty. The per-type detail object must match the type: bankUs for US accounts, bankIban for international IBAN accounts, or bankCanada for Canadian accounts.
Request
Response, 201 Created
accountNumberLast4. The account starts pending and flips to active (usable on quotes) or failed. Wait for active before quoting against it.
Beneficiary identity on a US bank payout
When the destination is a counterparty-ownedbankUs account, OMS registers the beneficiary with the paying bank under the counterparty’s own structured identity: the entityType, the matching name fields (firstName and lastName, or businessName), and the counterparty’s address. The customer’s identity is not used for a counterparty-owned account. Register US bank counterparties with the complete entityType, name, and address fields shown in Step 1; a legacy counterparty missing entityType (predating the structured name split) cannot be used as a bankUs destination and is rejected with 422 accountTypeRequirementsNotMet. A payout the receiving bank returns as a payee-name mismatch flips the external account to invalid with invalidReason: payeeNameMismatch.
Step 3: Create the quote
The quote is identical to any other bank payout: thesource is the customer’s OMS wallet, and the destination is the counterparty’s registered bank account referenced by its ext_bankUs_ ID.
party block, so you can render who is being paid without extra reads:
party.id for the recipient’s full record (entity type, address, contact fields). All amounts, fees, and rates live in the top-level pricing object. The direction is inferred as cryptoToFiatAccount.
Step 4: Execute and track
Accept the quote withPOST /transactions and the quoteId:
processing and moves to completed when the bank payout settles, or failed with an error object. The payout leg surfaces granular progress in subStatus: processing.awaitingFiatOut while the payout is queued, then processing.fiatOut once it is in flight. Poll GET /transactions/{transactionId} or subscribe to transaction.statusChanged; see the webhook events catalog for the full list.
To reconcile a payout run, filter GET /transactions?customerId=...: each transaction’s destination party.id is the counterparty it paid (relationship: "externalRegistered").
Payouts are sent from named accounts: the recipient’s bank statement shows the paying customer’s own legal-entity name as the sender, not Polygon and not your platform.
Managing counterparties
GET /counterparties?customerId=...lists a customer’s counterparties (customerIdis required). Paginate withlimit,startingAfter, andendingBefore.GET /counterparties/{counterpartyId}fetches one by ID.PATCH /counterparties/{counterpartyId}partially updates identity fields; omitted fields are unchanged.entityTypeis immutable, and bothentityTypeand the composednamefield are rejected with400. Name updates go through the split fields for the storedentityType(firstNameandlastNameforindividual;businessNameforbusiness); the server recomposes and echoes backname. A name field that does not belong to the storedentityTypereturns422 validationError.DELETE /counterparties/{counterpartyId}soft-deletes. It returns409while the counterparty still owns active external accounts: delete those accounts first, then delete the counterparty.
Related
- B2B payouts: running payouts to many recipients
- Bank transfers: rails, per-type bank fields, and registration details
- External accounts: the full registration and lifecycle model
- Crypto to fiat: the underlying quote and transaction flow