Skip to main content
A transaction moves money through OMS. You specify a source instrument and a destination instrument on the quote; OMS infers the direction from those two sides and reports it on the quote as sourceToDestination. There is no settable transaction “type”. Standard sends and payouts follow a two-step flow: create a quote to lock pricing, then create a transaction to execute it. Cash-in and auto-created transactions (virtual accounts, deposit addresses) skip the quote step.

Direction (sourceToDestination)

The quote’s sourceToDestination field is a composite of the source and destination instrument categories, inferred from each side. Filter GET /transactions by the same values:

Two-step flow

The quote locks the exchange rate and fee breakdown for a short validity window: 30 seconds by default, configurable per project. To execute it, call POST /transactions with a body of { "quoteId": "qt_..." }. A quote that has expired returns 422 QuoteExpired, and one already used returns 422 QuoteConsumed; create a new quote in either case. In the quote request, set amount on exactly one side, source or destination; OMS calculates the other. See the send from a wallet guide for full requests. The quote step also validates that the customer holds the endorsements the requested instruments require. A customer without an active cryptoCustody endorsement on a flow that moves funds through an OMS custodial wallet gets 403 KYCRequired at POST /quotes, rather than at POST /transactions. Resolve the customer’s endorsements before quoting; see Endorsements.

Quote object

status is open while the pricing is locked, then accepted once a transaction is created from it, or expired. Unlike other resources, a quote carries both customerId and a customer summary with the customer’s name.

Pricing

Every quote, transaction, and cash-in renders pricing in one shape: Every amount is a decimal string padded to the asset’s own precision, for example six decimals for usdc and two for usd. The same amount renders the same string in every response and webhook. Do not trim or re-round the value; display it as received or format it with your locale.

Instrument shape

The source and destination are typed instruments discriminated by type. There are ten arms: walletCrypto, walletFiat, walletExternal, bankUs, bankIban, bankCanada, card, cash, and the auto-created-flow arms depositAddress and virtualAccount. Each instrument carries type, asset, and, where applicable, id, network, owner, and details:

Owner, precursor, and payout origin

Transaction responses carry three flat reference objects that identify who owns a non-customer account, what created the transaction, and whose name the payout goes out under:
Dereference the referenced resource by id to retrieve richer detail. Bank account numbers, routing numbers, and BICs are not returned on payoutOrigin; read them from the funding virtual account or external account directly. payoutOrigin is left out when nothing is paid out to a bank account, a card, or a wallet, so cash payouts and fiat-wallet credits carry none; a cash payout carries its pickupCode on the cash destination instead. The same payoutOrigin is carried at the top level of the transaction, the deposit address, and the virtual account.

Rendering an instrument

The typed details object on each instrument carries the fields you need to label the side in your UI. Instruments carry no pre-formatted display string, so you choose the label that fits your layout. Common recipes: A counterparty-owned bank account also carries owner.name with the counterparty’s name. details.holderName carries the account holder: the counterparty’s name, or the sender’s name when the rail carries it. A customer’s own name lives on the customer resource.

Bank memo

Bank source and destination instruments carry an optional memo field inside details on transaction responses. When populated, it echoes the wire, ACH, or SWIFT memo that actually travelled on the rail:
OMS echoes only memos that a sender supplied. Any default text OMS may compose on behalf of a rail (for example, a SELL PROCEEDS line) never appears in the response. The field is present on GET /transactions/{transactionId}, on every entry of GET /transactions, and on transaction.statusChanged webhook payloads. Non-bank arms do not carry a memo.

Transfers and rail tracking

A transaction’s transfers array lists each movement of its money on one rail, in creation order. Each entry names the transfer id (trf_ prefix), its role, its rail, and its attempt:
The array is left out until the first transfer exists. Fetch a transfer with GET /transfers/{transferId} to read the rail references in its tracking object: tracking is left out until OMS holds a reference. The transfer also carries its own status, amount, from and to instruments, and, for an ACH payout, the secCode and companyDiscretionaryData it was sent with. See Transfers for roles, attempts, and transfer statuses.

Customer reference

Every transaction, deposit address, virtual account, cash-in, external account, counterparty, and customer wallet carries a customer reference that identifies the owning customer in one hop:
The field is populated on every GET, list, and POST/PATCH response, and on every webhook payload. Dereference the customer by id to read their name, status, and endorsements; names are not copied onto these resources. Quotes are the exception: they carry customerId and a customer summary that includes the name.

Transaction statuses

Every transaction also carries a dotted subStatus (for example processing.fundsPulled) that refines its status. Tolerate sub-statuses you do not recognize by falling back to the part before the dot. See the transaction lifecycle for every sub-status. OMS fires transaction.created once and transaction.statusChanged on every status or sub-status change, so your handler branches on the payload’s status and subStatus fields. See Receiving webhooks to subscribe.

Developer fees

Developer fees are configured per project; the quote request has no fee field. They as entries in the pricing.fees array with type: "developer", carrying the amount, asset, and side of the fee.

Timestamps

Every date-time rendered on these resources (and on their v1 webhook payloads) is UTC with three-digit millisecond precision, for example 2026-10-16T10:35:00.123Z. Parse the value with any ISO 8601 parser; do not depend on a fixed character count.

Key operations

GET /transactions returns results newest-first and filters by status, sourceToDestination, customerId, the originating instrument (walletId, virtualAccountId, depositAddressId, cashInId), and inclusive createdAfter/createdBefore date bounds. Paginate with limit (default 50, maximum 100; values outside 1 to 100 are clamped), startingAfter, and endingBefore. Pass a page’s nextCursor as startingAfter to read the next page, or its previousCursor as endingBefore to read the previous one. Cursors are opaque; do not parse them.
POST /quotes and POST /transactions require an Idempotency-Key header. Use a stable key tied to your internal order ID so a retry after a network failure cannot execute twice. Reusing a key with a different body returns 422 IdempotencyKeyReuse.