The entity model
Customer. A customer has zero or more wallets. Counterparties, virtual accounts, deposit addresses, quotes, and transactions are also scoped to a customer; external accounts belong to a customer or to one of its counterparties. Webhook subscriptions are managed through the API independently of any customer.
Customers
A customer is an identity record. Before a customer can move money, OMS must grant endorsements that unlock specific capabilities.
Customers are created via
POST /customers with a type (individual or business) and an optional endorsements array. Endorsements are granted after KYC/KYB verification; a webhook fires as they change.
Wallets
The Payments APIwallet resource is custodial: OMS manages the keys, and they never leave OMS infrastructure. Wallets hold stablecoin balances (USDC by default) and are the source or destination for most transactions.
Wallets are created under the customer with POST /customers/{id}/wallets (body specifies asset and chain) and listed with GET /wallets?customerId=.... Each wallet holds a single asset on one chain and has an onchain address. GET /wallets/{id}/balance returns that wallet’s current balance for its asset and chain.
A customer can also hold a fiat wallet: a USD balance at OMS’s partner bank, created with POST /wallets (type: fiat). It is funded through a virtual account or deposit address and withdrawn with a quote and transaction, like a custodial wallet.
For user-controlled keys (onchain consumer apps, agentic flows), use OMS non-custodial wallets. The two models can be combined.
Virtual accounts
A dedicated bank account number assigned to a customer. When fiat arrives via a supported bank rail, OMS automatically creates a transaction that delivers the funds to the account’s destination: USDC to a wallet (fiatAccountToCrypto), or USD to a bank account or fiat wallet (fiatAccountToFiatAccount).
Create one with POST /virtual-accounts; the body specifies the customerId, the source asset and network, a destination (an OMS wallet, a registered external wallet or bank account by ID, or the customer’s fiat wallet), and the account type (bankUs). The response’s depositInstructions is left out until the underlying deposit account is provisioned. Virtual accounts support list, get, and update (GET /virtual-accounts, GET/PATCH /virtual-accounts/{id}). Deletion is asynchronous: DELETE /virtual-accounts/{id} returns 204, the status reads deleting, and it finalizes to deleted once the underlying account closes.
Deposit addresses
A persistent onchain address. When crypto arrives, OMS automatically creates and executes a transaction that delivers fiat to a registered bank account or fiat wallet, or forwards the crypto to a registered external wallet. Create one withPOST /deposit-addresses; the body specifies the customerId, the expected source asset (usdc or usdt) and network, and the destination: a registered bank external account, the customer’s fiat wallet, or a registered walletExternal external account. The response’s depositInstructions carries the accepted asset and network from creation; its address is left out until the inlet address is issued. Deposit addresses support list, get, and update (GET /deposit-addresses, GET/PATCH /deposit-addresses/{id}); there is no delete. Deposit addresses must be enabled for your project: contact us to enable them.
Counterparties
A person or business a customer transacts with, scoped to that customer. Create one withPOST /counterparties; the body requires customerId, a required entityType discriminator (individual or business), and the matching name fields for that type. For entityType: "individual", supply firstName and lastName, with optional dateOfBirth. For entityType: "business", supply businessName. An address is required unless your project is configured not to require one; it takes line1, city, zipCode, and a two-letter ISO country, plus state for a US address and an optional line2. Optional identity fields: email, phone, taxId, nationality, metadata. Responses echo the split fields and add a server-composed, read-only name (businessName, or firstName and lastName joined by a space).
Counterparties support full CRUD. Listing accepts an optional customerId query parameter (GET /counterparties?customerId=...). entityType is immutable, so PATCH rejects both entityType and name with 400; name updates go through the split fields for the stored entity type. A counterparty can own external accounts; DELETE /counterparties/{id} returns 409 while the counterparty still owns active or pending external accounts.
External accounts
A registered bank account, debit card, or external wallet, owned by a customer or by one of the customer’s counterparties. External accounts are referenced by ID as the source for on-ramps or the destination for off-ramps. Register one withPOST /external-accounts. The body specifies the owner ({ kind: "customer", customerId } or { kind: "counterparty", counterpartyId }), a type (bankUs, bankIban, bankCanada, or walletExternal), and exactly one per-type details object matching the type. Debit cards register through their own endpoint, POST /external-accounts/cards; the generic endpoint rejects type: card with CardMustUseCardEndpoint. See Debit cards. GET /external-accounts accepts optional customerId and counterpartyId filters; PATCH updates label and metadata; DELETE removes the registration.
Every external account ID uses the ext_ prefix, whatever the instrument. Read the type field to tell which instrument an account is.
Transactions
A transaction is the record of money moving through OMS. Its direction is inferred from the source and destination instruments:cryptoToCrypto, cryptoToFiatAccount, cryptoToCash, fiatAccountToCrypto, fiatAccountToFiatAccount, or cashToCrypto. Quotes report it as sourceToDestination, and GET /transactions filters on it. Each movement of the transaction’s money on one rail is a transfer, listed in the transaction’s transfers.
Most transactions are created for you. The resource that receives the funds decides how:
Auto-created transactions skip the quote step and go directly to
processing, because OMS cannot lock pricing before the funds arrive.
Quotes
A quote locks pricing and returns the full fee breakdown before you move money. You only create one when the source is an OMS wallet balance or a debit card:POST /quotes: lock pricing and return the fee breakdown.POST /transactions: execute against the locked quote.
pricing.fixedAmountSide indicates which side of the trade was specified.
Cash-ins
A code-based deposit flow for in-person cash at retail locations. Creates a deposit code the customer presents at a partner location. OMS converts the deposited cash to USDC and auto-creates a transaction. No upfront amount is required.Webhooks
API-managed event subscriptions with full CRUD. Register a subscription withPOST /webhooks (body { url, subscriptions }) and OMS delivers events as state changes occur; each delivery carries the full object under data so your handler branches on data.status. Subscribe to specific event types, or pass ["*"] in subscriptions to receive all of them. Subscriptions are created, listed, updated, and soft-deleted through the API (GET/PATCH/DELETE /webhooks/{webhookId}) as well as in the dashboard, and lifecycle actions cover enable, disable, rotate-key, and test at POST /webhooks/{webhookId}/enable | /disable | /rotate-key | /test. Delivery history is inspectable at GET /webhooks/{webhookId}/deliveries and requeueable at POST /webhooks/{webhookId}/deliveries/retry. The create response returns a signingKey once for verifying the Webhook-Signature header; rotate it with POST /webhooks/{webhookId}/rotate-key.