> ## Documentation Index
> Fetch the complete documentation index at: https://docs.polygon.technology/llms.txt
> Use this file to discover all available pages before exploring further.

# Fiat to crypto

> How to bring fiat into a customer's custodial wallet.

<Note>
  **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](https://info.polygon.technology/get-early-access?utm_source=docs\&utm_medium=card\&utm_campaign=oms_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](/payments/get-started) for the full flow.
</Note>

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](/payments/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

<CodeGroup>
  ```bash Sandbox theme={null}
  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
    }'
  ```

  ```bash Production theme={null}
  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
    }'
  ```
</CodeGroup>

* `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](/api-reference/guide-cash-in).

## 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:

<CodeGroup>
  ```bash Sandbox theme={null}
  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"
    }'
  ```

  ```bash Production theme={null}
  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"
    }'
  ```
</CodeGroup>

* `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`:

<CodeGroup>
  ```bash Sandbox theme={null}
  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" }
    }'
  ```
</CodeGroup>

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](/payments/guides/virtual-accounts).

## 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`.
