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.
This guide walks through registering a new end user in OMS, acquiring the endorsements required to unlock money movement, and provisioning a custodial wallet. Every transaction, deposit address, and virtual account in OMS belongs to a customer record, so this is the starting point for any integration.

Architecture

Customer onboarding flow
1AppOMSPOST /customers
2OMSApp{id: “cst_…”, endorsements: []}
3AppUserCollect identity documents
4UserAppKYC/KYB data
5AppOMSSubmit verification (KYC flow)
6OMSAppWebhook: customer.endorsement.granted
7AppOMSPOST /customers/{id}/wallets
8OMSPolygonDerive wallet address
9OMSApp{id: “wlt_…”, address: “0x…“}
10AppOMSPOST /customers/{id}/external-accounts (optional)
11OMSApp{id: “cpUsBank_…”}

Prerequisites

  • An OMS API key.
  • Webhook subscriptions configured for customer.endorsement.* events in the OMS Dashboard.

Step 1: Create a customer

Register the end user with a POST /customers call. OMS creates the record and returns an ID.

Request

POST /v0.9/customers
Authorization: Bearer {api_key}
Idempotency-Key: cst-alice-001
Content-Type: application/json
{
  "type": "individual",
  "firstName": "Alice",
  "lastName": "Martin",
  "email": "alice@example.com",
  "phone": "+12125551234",
  "birthDate": "1990-05-15",
  "nationality": "US",
  "externalId": "usr_7890",
  "signedAgreement": true
}
Required fields: type must be individual. Only the individual customer type is supported in the v0.9 API. All other fields are optional but needed before endorsements can be granted. 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

{
  "id": "cst_01H9Xa...",
  "object": "customer",
  "type": "individual",
  "firstName": "Alice",
  "lastName": "Martin",
  "email": "alice@example.com",
  "externalId": "usr_7890",
  "status": "active",
  "endorsements": [],
  "createdAt": "2024-01-15T10:00:00Z"
}
The endorsements array is empty at creation. No money movement is possible until endorsements are granted.

Step 2: Collect identity and get endorsements

OMS uses an endorsement model to gate access to financial operations. The three endorsements are:
EndorsementUnlocks
basicStandard operations. Required for all transactions.
cryptoCustodyCrypto custody and advanced wallet features.
usdUSD stablecoin operations, including virtual accounts.
Your KYC/KYB flow submits identity documents to OMS for review. When verification completes, OMS grants the appropriate endorsements and fires a webhook.

Webhook: customer.endorsement.granted

{
  "event": "customer.endorsement.granted",
  "data": {
    "id": "cst_01H9Xa...",
    "object": "customer",
    "endorsements": ["basic", "cryptoCustody", "usd"]
  }
}
Check endorsements in this payload to confirm which operations are now available for the customer.

Step 3: Provision a wallet

Once the customer has at least the cryptoCustody endorsement, create a custodial wallet. OMS derives the onchain address and manages the keys.

Request

POST /v0.9/customers/cst_01H9Xa.../wallets
Authorization: Bearer {api_key}
Idempotency-Key: wlt-alice-polygon-001
Content-Type: application/json
{
  "asset": "usdc",
  "chain": "polygon"
}
The customer is identified in the path. The request body declares the asset and chain the wallet will hold.

Response, 201 Created

{
  "id": "wlt_01H9Xb...",
  "object": "wallet",
  "customerId": "cst_01H9Xa...",
  "address": "0xBEEF4a2c891D56e72b67a3f21d0cf94F1D7c5911",
  "asset": "usdc",
  "chain": "polygon",
  "status": "active",
  "createdAt": "2024-01-15T10:01:00Z"
}
The address is the onchain address where deposits for this customer will be received. Store the wlt_ ID, you will use it as the source or destination in quotes.

Step 4: Add an external bank account (optional)

External bank account and card registration is not yet available in the OMS API. To be notified when it launches, register your interest.

Register interest

Share your use case and we’ll reach out when external bank account and card registration is available.
The pattern below describes the intended shape so you can plan your integration. The schema lookup (GET /external-accounts/schemas) and the registration call (POST /customers/{id}/external-accounts) are not yet available in the OMS API. If the customer will receive fiat payouts, register their bank account as an external account. Use GET /external-accounts/schemas to get the required fields for the target country and currency.

Get the schema

GET /v0.9/external-accounts/schemas?country=US&currency=usd
Authorization: Bearer {api_key}
The schemas endpoint accepts country and currency as optional query params. The response describes which fields each supported rail requires. Render those fields as a form and collect the data from the customer.

Register the account

POST /v0.9/customers/cst_01H9Xa.../external-accounts
Authorization: Bearer {api_key}
Content-Type: application/json
{
  "type": "usBank",
  "network": "ach",
  "owner": {
    "name": "Alice Martin",
    "type": "individual"
  },
  "bankDetails": {
    "accountNumber": "000123456789",
    "routingNumber": "021000021",
    "accountType": "checking",
    "bankName": "Chase"
  }
}
Per-rail bank fields live inside bankDetails and are validated against the response from GET /external-accounts/schemas. The network value (ach, wire, swift, etc.) declares the rail.
{
  "id": "cpUsBank_01H9Xs...",
  "object": "counterparty.usBank",
  "customerId": "cst_01H9Xa...",
  "type": "usBank",
  "network": "ach",
  "status": "verified",
  "owner": { "name": "Alice Martin", "type": "individual" },
  "bankDetails": {
    "accountNumber": "****6789",
    "routingNumber": "021000021",
    "accountType": "checking",
    "bankName": "Chase"
  },
  "createdAt": "2024-01-15T10:02:00Z"
}
The cpUsBank_ ID identifies the bank account for use elsewhere in OMS.

What’s next

With a customer, wallet, and optional 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 (early access).
  • Create a deposit address to receive crypto from external wallets (early access).
  • Send funds out of the wallet with the send guide: crypto destinations today, bank payouts in early access.