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.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.
Fiat-in does not use the quote and transaction pattern. A quote’s source is always an OMS wallet or a card, so there is no way to fund a quote with incoming fiat. Instead, fiat enters through a fiat-in product that auto-creates a transaction once the money arrives:
  • Cash-in: a customer deposits physical cash at a retail location, and OMS delivers USDC to the destination wallet. The auto-created transaction has direction cashToCrypto.
  • Virtual accounts: each customer gets a dedicated bank account number; an ACH, wire, or SWIFT deposit auto-converts to USDC. The auto-created transaction has direction fiatAccountToCrypto.
Both paths deliver to a crypto destination (an OMS wallet) and let OMS create the transaction for you. You never call POST /quotes for fiat-in. The exception is debit card funding: a registered card is a valid quote source, so pull-from-card funding uses the standard quote and transaction flow rather than a fiat-in product. See Debit cards for the card source shape and settlementType.

Cash-in

Cash-in is a fiat-to-crypto path in the OMS API. You create a cash-in record that names the customer, the destination wallet, and the cash location where the customer will deposit. OMS returns estimated pricing upfront and finalizes it once the customer deposits cash at the counter.

Create a cash-in

curl -X POST https://sandbox-api.polygon.technology/v0.10/cash-ins \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ci-alice-usdc-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "cash": {
      "locationId": "loc_01H9Xd...",
      "locationReference": "R1JFRU5ET1QtMjQzNDpsYXQ9MzguNDk1MTMzLGxuZz0tMTIxLjUwNTMzNg=="
    },
    "source": {
      "asset": "usd",
      "indicatedAmount": "100.00"
    },
    "destination": {
      "asset": "usdc",
      "network": "polygon",
      "wallet": { "id": "wlt_01H9Xb..." }
    },
    "sponsorGas": true
  }'
curl -X POST https://api.polygon.technology/v0.10/cash-ins \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: ci-alice-usdc-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "cash": {
      "locationId": "loc_01H9Xd...",
      "locationReference": "R1JFRU5ET1QtMjQzNDpsYXQ9MzguNDk1MTMzLGxuZz0tMTIxLjUwNTMzNg=="
    },
    "source": {
      "asset": "usd",
      "indicatedAmount": "100.00"
    },
    "destination": {
      "asset": "usdc",
      "network": "polygon",
      "wallet": { "id": "wlt_01H9Xb..." }
    },
    "sponsorGas": true
  }'
  • customerId: the customer making the deposit. Needs the usd endorsement to be ACTIVE.
  • cash.locationId and cash.locationReference: the deposit location, taken from the locId and cashLocationReference fields of the GET /cash-locations response.
  • source.asset: always usd.
  • source.indicatedAmount: the expected deposit amount, used for upfront pricing estimates. Optional; the actual amount is whatever the customer deposits at the counter.
  • destination: the crypto destination. Set asset and network, and identify the OMS wallet with wallet.id.
  • sponsorGas: when true, OMS absorbs the gas cost of delivering crypto. At launch gas is always sponsored.
OMS returns the cash-in with estimated pricing in the top-level pricing object. When the customer deposits cash, OMS recalculates pricing on the actual amount, creates the cashToCrypto transaction, and delivers USDC to the destination wallet, firing cashIn.completed and the transaction.fiatToCrypto.* events along the way. For the full lifecycle, pricing fields, location lookup, and webhook events, see the Cash-in guide.

Virtual accounts

Virtual accounts give each customer a dedicated bank account number. When the customer sends an ACH, wire, or SWIFT transfer to that number, OMS auto-converts the deposit to USDC and creates the fiatAccountToCrypto transaction. This suits recurring funding, where a customer tops up the same account repeatedly rather than deposits cash. Account provisioning requires the customer’s usd endorsement to be ACTIVE, and virtual accounts must be enabled for your project (contact us to enable them).

Create a virtual account

Create the account with POST /virtual-accounts, naming the customer, the fiat source, and the wallet that receives the converted crypto:
curl -X POST https://sandbox-api.polygon.technology/v0.10/virtual-accounts \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: va-alice-usd-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "source": { "asset": "usd", "network": "usBank" },
    "destination": {
      "type": "walletOms",
      "details": {
        "id": "wlt_01H9Xb...",
        "asset": "usdc",
        "network": "polygon"
      }
    },
    "accountHolder": "customer",
    "type": "bankUs",
    "bankMemo": "Alice funding"
  }'
curl -X POST https://api.polygon.technology/v0.10/virtual-accounts \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: va-alice-usd-001" \
  -d '{
    "customerId": "cst_01H9Xa...",
    "source": { "asset": "usd", "network": "usBank" },
    "destination": {
      "type": "walletOms",
      "details": {
        "id": "wlt_01H9Xb...",
        "asset": "usdc",
        "network": "polygon"
      }
    },
    "accountHolder": "customer",
    "type": "bankUs",
    "bankMemo": "Alice funding"
  }'
  • source: the fiat side; asset must be usd and network must be usBank today.
  • destination: either walletOms with the customer’s wallet id, asset, and network, or walletExternal with the id of a registered external wallet account (ext_wlt_...) plus asset and network. Raw blockchain addresses are not accepted.
  • accountHolder: must be customer. type: must be bankUs.
  • bankMemo: an optional memo the customer can include on the transfer.
The 201 response carries the va_ ID with bankDetails set to null: OMS provisions the underlying deposit account asynchronously. Subscribe to the virtualAccount.provisioned event, or poll GET /virtual-accounts/{virtualAccountId} until status is active and bankDetails carries the domestic and SWIFT deposit instructions to share with your customer. When a transfer arrives, OMS credits the destination wallet and creates the transaction automatically, with no additional call from you; track it through the virtualAccount.deposit.* and transaction.fiatToCrypto.* events.

Test in sandbox

In sandbox you can rehearse the full flow without a real bank transfer. Simulate an inbound deposit with POST /virtual-accounts/{virtualAccountId}/simulate, setting rail to ach_in, wire_in, or swift_in:
curl -X POST https://sandbox-api.polygon.technology/v0.10/virtual-accounts/va_01H9Xe.../simulate \
  -H "Authorization: Bearer {accessToken}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: va-sim-alice-001" \
  -d '{
    "rail": "ach_in",
    "amount": { "value": "10000", "currency": "USD" }
  }'
The simulate endpoint is sandbox-only; it returns 404 in production. It drives the same auto-created transaction and webhook flow (transaction.fiatToCrypto.*) a real deposit would, so you can test reconciliation before going live. For the full lifecycle, statuses, updates, and deletion, see the Virtual accounts guide.

Key points

  • Fiat-in never uses a quote. A quote’s source is always an OMS wallet or a card, so incoming fiat cannot fund a quote. Use cash-in or virtual accounts, which auto-create the transaction for you.
  • The destination is a crypto wallet. Cash-in identifies it with destination.wallet.id; a virtual account uses the side shape, with destination.type set to walletOms or walletExternal and the wallet ID in destination.details.id.
  • Direction is inferred. Cash-in produces a cashToCrypto transaction; a virtual account produces a fiatAccountToCrypto transaction.
  • The usd endorsement gates fiat-in. Both cash-in and virtual accounts require the customer’s usd endorsement to be ACTIVE.