Skip to main content
OMS gives you a single API for moving money between fiat and stablecoins. This guide takes you from zero to a working transaction in four steps.
1

Request access and authenticate

OMS is in early access. Start by requesting access from the dashboard.

Request OMS access

Submit your details to get sandbox credentials.
Once approved, open the OMS Dashboard and navigate to API Keys. Generate a new key and store the secret immediately, it is shown only once.
Treat your API key secret like a password. If it is ever compromised, revoke the key from the dashboard and generate a new one immediately.
You do not send the API key directly on requests. Exchange the key and secret for a short-lived bearer token at POST /auth/token, then send that token on every other call.
curl -X POST https://sandbox-api.polygon.technology/v0.9/auth/token \
  -H "Content-Type: application/json" \
  -d '{
    "apiKey": "{api_key}",
    "apiSecret": "{api_secret}"
  }'
{
  "accessToken": "eyJhbGc...",
  "tokenType": "bearer",
  "expiresIn": 3600,
  "expiresAt": "2024-01-15T11:00:00Z"
}
The token is valid for 60 minutes. Send the accessToken as a bearer token on every other request:
Authorization: Bearer {token}
When a request returns 401, the token has expired. Exchange your key for a fresh one and retry.
2

Create a customer

Every wallet, transaction, and payment route in OMS belongs to a customer record. Create one before anything else. To onboard a customer who can move USD or use cash services, send the full set of identifying fields, not just a name.
curl -X POST https://sandbox-api.polygon.technology/v0.9/customers \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: cst-first-customer-001" \
  -d '{
    "type": "individual",
    "firstName": "Jane",
    "lastName": "Smith",
    "email": "jane@example.com",
    "phone": "+12125551234",
    "birthDate": "1990-05-15",
    "nationality": "US",
    "residentialAddress": {
      "line1": "123 Main St",
      "city": "New York",
      "state": "NY",
      "country": "US",
      "zipCode": "10001"
    },
    "identifyingInformation": [
      { "type": "ssn", "issuingCountry": "US", "number": "123-45-6789" }
    ],
    "endorsements": ["basic", "cryptoCustody", "usd"]
  }'
{
  "id": "cst_01H9Xa...",
  "object": "customer",
  "endorsements": [
    { "type": "basic", "status": "PENDING" },
    { "type": "cryptoCustody", "status": "PENDING" },
    { "type": "usd", "status": "PENDING" }
  ],
  "createdAt": "2024-01-15T10:00:00Z"
}
Store the cst_ ID; you pass it to every wallet, quote, and transaction. The requested endorsements appear with their status and move to ACTIVE once review passes.
The API accepts a customer with only type, but a customer created without the identifying fields below cannot be provisioned to move fiat. The record is created, yet calls that need a provisioned fiat account (reading a balance, cash-in, or a fiat transaction) fail with provider account not provisioned for this customer. For USD and cash flows, always provide:
  • A structured residentialAddress (the object above, not a free-text string)
  • phone in E.164 format
  • birthDate
  • A government ID in identifyingInformation (for US customers, an ssn or itin)
Supply these at creation, or add them later with PATCH /customers/{customerId}. Products that never touch fiat rails may not need them.
In sandbox, endorsements are auto-approved so you can test without a live KYC integration. Provisioning still reads the identifying fields above, so include them in sandbox too, otherwise balance and money-movement calls return provider account not provisioned.
3

Provision a wallet

Create a custodial wallet for the customer. The customer ID goes in the path, and the body specifies the asset and chain to hold. OMS derives the onchain address and manages the keys, no wallet SDK or user signing required.
curl -X POST https://sandbox-api.polygon.technology/v0.9/customers/cst_01H9Xa.../wallets \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: wlt-first-wallet-001" \
  -d '{
    "asset": "usdc",
    "chain": "polygon"
  }'
{
  "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 Polygon address. The wlt_ ID is what you pass as source or destination in quotes and transactions.
4

Run your first transaction

The cash-in flow is available in the OMS API today and is the recommended path for your first run. The bank transfer flow is documented for when external accounts are available to you.
Let a customer deposit physical cash at a retail location and receive USDC in their wallet.
curl -X POST https://sandbox-api.polygon.technology/v0.9/cash-ins \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ci-first-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "destination": {
      "wallet": { "wallet": "wlt_01H9Xb..." },
      "asset": "usdc",
      "network": "polygon"
    },
    "cash": {
      "cashLocationId": "loc_01H9Xl...",
      "cashLocationReference": "R1JFRU5ET1QtMjQzNDpsYXQ9..."
    },
    "source": { "asset": "usd", "indicatedAmount": "100.00" }
  }'
OMS returns a depositInstructions.code valid for 1 hour. The customer presents the code at the retail location, hands over cash, and USDC lands in their wallet automatically.See the Cash-in guide for the full flow.
5

Configure webhooks

OMS fires webhooks at every meaningful state change. You can poll GET /transactions/{id} instead, but webhooks are strongly recommended for production.Configure subscriptions in the OMS Dashboard under Webhooks, or create them via the API with POST /v0.9/webhooks and a url. The signing secret is returned once on creation, so store it immediately. Key events to subscribe to:
EventWhen it fires
transaction.fiatToCrypto.completedUSDC delivered to wallet
transaction.cryptoToFiat.completedFiat landed in bank account
cashIn.completedCash deposit processed
transaction.*.failedAny transaction failure
customer.endorsement.grantedKYC approved
Webhook payloads include the full object, so you rarely need to poll for additional data.
Use transaction.*.failed as a catch-all for failure events across all transaction types. The error.code field in the payload tells you what went wrong.

What’s next

Cash-in

Full walkthrough of the cash deposit flow, including deposit code generation and retail location selection.

Bank transfers

Move money between bank accounts and wallets in both directions using ACH and card rails.

Payments overview

How OMS handles payments, stablecoin settlement, and compliant fiat access end to end.

API reference

Complete endpoint reference for all OMS resources.