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 mutating request (POST and PATCH) also requires an Idempotency-Key header. See Get started for the full flow.Architecture
Prerequisites
- An OMS API key.
- Webhook endpoints registered with
POST /webhooks(or in the OMS Dashboard) if you plan to react to status changes as endorsements advance and transactions settle.
Step 1: Create a customer
Register the end user with aPOST /customers call. OMS creates the record and returns an ID. To onboard a customer who can move USD or use cash services, send the full set of identifying fields, not just a name, and request the endorsements the customer needs.
Request
type must be individual. Only the individual customer type is supported today.
endorsements declares which capabilities the customer needs: basic (identity and geo checks), cryptoCustody (custodial wallets that hold stablecoins), and usd (USD movement over fiat rails, including bank payouts, virtual accounts, and cash services). Requesting cryptoCustody or usd auto-includes basic. If you omit endorsements, OMS defaults to cryptoCustody and usd. Request endorsements through the Customers API.
externalId is the recommended way to link the OMS customer record to your internal user ID. Pass it at creation to avoid a separate update call later.
Response, 201 Created
status field is active or inactive; all the granularity lives in the per-endorsement status, which uses SCREAMING_CASE (INACTIVE, PENDING, ISSUES, ACTIVE, REJECTED, REVOKED_ISSUES, OFFBOARDED). An endorsement must reach ACTIVE before its capability unlocks. PII fields (birthDate, residentialAddress, ipAddress, identifyingInformation) are write-only: OMS accepts them but never returns them. A customer created without the identifying fields cannot be provisioned to move fiat.
Step 2: Collect identity and unlock endorsements
OMS uses an endorsement model to gate access to financial operations. An endorsement tracks the KYC and compliance status behind a capability. Request the endorsements a customer needs at creation, or add them later withPATCH /customers/{customerId}.
| Endorsement | Unlocks |
|---|---|
basic | Identity and geo-based compliance checks. Auto-included with any other endorsement. |
cryptoCustody | Custodial wallets that hold stablecoins. |
usd | USD movement over fiat rails: US bank payouts, virtual accounts, and cash services. |
ACTIVE. Provisioning reads the identifying fields above, so include them even in sandbox, where endorsements are auto-approved.
Endorsement names on the wire are
basic, cryptoCustody, and usd. An upcoming release adds a finer-grained set of endorsements for specific rails (for example, separate IBAN, Canadian bank, and card endorsements). Those are early access; contact us to enable them.Step 3: Provision a wallet
Once the customer’scryptoCustody endorsement is ACTIVE, create a custodial wallet with POST /customers/{customerId}/wallets. The path carries the customer ID; the body names the single asset and chain the wallet will hold. OMS derives the onchain address and manages the keys, with no wallet SDK or user signing.
Request
asset (usdc or usdt) on one chain (polygon, ethereum, base, or solana). To provision the same customer on another chain or asset, call the endpoint again with a different asset/chain pair.
Response, 201 Created
address is the onchain address where deposits for this customer arrive, and blockchainAsset resolves the on-chain identity (protocol, chain ID, and token contract). type is internal for OMS-managed wallets; status is active, suspended, or closed. Store the wlt_ ID, you will use it as the source or destination in quotes and transactions. Read the current balance with GET /wallets/{walletId}/balance, and list a customer’s wallets with GET /customers/{customerId}/wallets.
Step 4: Register an external bank account
If the customer will receive fiat payouts, they need a registered external bank account. External accounts are referenced everywhere in OMS by their ID, with a per-type prefix:ext_bankUs_ for US bank accounts, ext_bankIban_ for IBAN accounts, ext_bankCa_ for Canadian accounts, ext_card_ for debit cards, and ext_wlt_ for externally-custodied wallets.
Register the account with POST /external-accounts. The body carries an owner, a type, and exactly one per-type object named after the type (here bankUs). Supplying a per-type object that does not match type is rejected with 422.
Request
bankUs, accountNumber and routingNumber are required; accountType (checking or savings) and bankName are optional. IBAN accounts use a bankIban object (iban and BIC required) and Canadian accounts use a bankCanada object (institutionNumber, transitNumber, and accountNumber required).
Response, 201 Created
pending and flips to active once provisioning completes, or failed if the provider rejects it. accountNumber is write-only: reads expose only bankUs.accountNumberLast4, never the full number. List a customer’s accounts with GET /external-accounts?customerId=... (the customerId query parameter is required).
Once the account is active, reference its ID in the destination of a quote to pay out to that account:
usd endorsement to be ACTIVE. See Send from a wallet for the full payout flow.
To pay a third-party recipient rather than the customer, create a counterparty with
POST /counterparties (customerId and name required), then register the recipient’s bank account with owner: { "kind": "counterparty", "counterpartyId": "..." }. Debit cards (type: "card") and external wallets (type: "walletExternal") register through the same POST /external-accounts endpoint; there is no separate cards endpoint.What’s next
With a customer, wallet, and registered external account in place, you can:- Fund the wallet with a cash-in at a retail location.
- Set up a virtual account to accept recurring ACH deposits.
- Create a deposit address to receive crypto from external wallets.
- Send funds out of the wallet with the send guide: crypto addresses or bank payouts.