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.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.
This guide shows you how to pay someone who is not your customer: a vendor, a payee, or any other third party. In OMS, that recipient is a counterparty, an entry in the customer’s address book that can own registered external accounts. Beneficiaries never need an OMS account of their own. The flow has four steps: create the counterparty, register their bank account, create a quote, and execute the transaction.

Prerequisites

  1. A customer with a cst_ ID and the usd endorsement: the payout is funded from this customer’s wallet.
  2. A funded OMS wallet with a wlt_ ID holding enough USDC to cover the payout.
  3. 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" requires firstName and lastName, with optional dateOfBirth.
  • entityType: "business" requires businessName.
Other identity fields are optional but improve compliance screening: 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

The 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.
For an 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 with POST /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

The full account number is write-only; reads return 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-owned bankUs 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: the source is the customer’s OMS wallet, and the destination is the counterparty’s registered bank account referenced by its ext_bankUs_ ID.
On the response, OMS resolves the recipient’s identity onto the destination’s party block, so you can render who is being paid without extra reads:
Dereference the counterparty by 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 with POST /transactions and the quoteId:
The transaction starts at 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 (customerId is required). Paginate with limit, startingAfter, and endingBefore.
  • GET /counterparties/{counterpartyId} fetches one by ID.
  • PATCH /counterparties/{counterpartyId} partially updates identity fields; omitted fields are unchanged. entityType is immutable, and both entityType and the composed name field are rejected with 400. Name updates go through the split fields for the stored entityType (firstName and lastName for individual; businessName for business); the server recomposes and echoes back name. A name field that does not belong to the stored entityType returns 422 validationError.
  • DELETE /counterparties/{counterpartyId} soft-deletes. It returns 409 while the counterparty still owns active external accounts: delete those accounts first, then delete the counterparty.