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

# Open Money Stack API

> How the Open Money Stack API works: core concepts, transaction types, and API structure.

## What Is the Open Money Stack API?

The Open Money Stack API (OMS) is a unified interface for moving money across fiat rails, blockchains, and currencies. One integration gives you access to bank transfers, card payments, cash networks, and multiple blockchains.

***

## Three Ways to Move Money

OMS provides three mechanisms for money movement, each suited to different use cases:

| Mechanism             | What It Does                                                                                                                                                                                                                                       | When to Use                                                                                  |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| **Transactions**      | Single endpoint for crypto-to-crypto, fiat-to-crypto, and crypto-to-fiat. OMS infers the type from the asset pair. Two-step flow: Create a Quote → Create a Transaction. Also includes **Cash-In**: a code-based flow for in-person cash deposits. | Amount known upfront (standard transactions) or determined at the counter (cash-in).         |
| **Deposit Addresses** | Persistent deposit configurations for crypto sources that auto-create transactions when funds arrive. Destinations can be crypto or fiat.                                                                                                          | "Send any amount to this address" flows. Amount unknown until deposit.                       |
| **Virtual Accounts**  | Dedicated bank account numbers that auto-convert incoming fiat to crypto.                                                                                                                                                                          | Customers who need a persistent bank account number for recurring fiat-to-crypto conversion. |

***

## How a Transaction Works

1. **Create a Quote.** `POST /quotes` with source, destination, and amount. OMS locks pricing and returns the quote in `open` status with a full fee breakdown.
2. **Create a Transaction.** Review the pricing, then call `POST /transactions` with the quote ID. OMS begins execution and the transaction moves to `processing`.
3. **Track.** Poll `GET /transactions/{id}` or listen for webhook events.

The transaction type (`cryptoToCrypto`, `fiatToCrypto`, or `cryptoToFiat`) is inferred from the asset pair. If pricing expires before transaction creation, create a new quote.

Every transaction records a `senderCustomerId` (the quote owner) and, when it can be resolved, a `recipientCustomerId`. When you read transactions from a customer's perspective, with `GET /customers/{id}/transactions` or by passing `senderCustomerId` or `recipientCustomerId` to `GET /transactions/{id}`, the response adds a `role` field of `sender`, `recipient`, or `both`. This identifies that customer's side of the transaction, which is useful for two-sided flows such as remittances and B2B payouts where one transaction involves two customers.

See the [Fiat to Crypto](/payments/guides/fiat-to-crypto) and [Crypto to Fiat](/payments/guides/crypto-to-fiat) guides for full walkthroughs with request/response payloads and webhook events.

### Cash-In

Cash-In is a specialized transaction flow for in-person cash deposits at retail locations. Instead of the two-step quote/transaction flow, the developer creates a cash-in that generates a deposit code. The customer takes that code to a retail location, deposits cash, and OMS automatically converts it to crypto and delivers it to the destination wallet.

1. **Create.** `POST /cash-ins` with a destination wallet, location, and developer fees. OMS returns a deposit code valid for 1 hour.
2. **Deposit.** The customer presents the code at the location and deposits any amount of cash.
3. **Convert.** OMS receives the deposit, applies fees (OMS + developer), converts to the destination asset, and delivers crypto. The cash-in status moves to `completed` and a transaction record is auto-created.

The code can be refreshed via `POST /cash-ins/{id}/refresh` if it expires before the customer deposits. No source amount is specified at creation: the customer decides how much to deposit at the counter. The creation response includes an `omsFeeSchedule` so the developer can show the customer the fee structure before they visit the location.

See the [Cash-In](/api-reference/guide-cash-in) guide for a full walkthrough.

***

## Deposit Addresses

<Note>
  Deposit addresses and virtual accounts are not yet available in the OMS API. They are provisioned by Polygon through early access. In sandbox, a simulate endpoint exists for each (`POST /deposit-addresses/{id}/simulate` and `POST /virtual-accounts/{id}/simulate`) so you can test the auto-created transaction flow. To be notified when management launches, register your interest.

  <Card title="Register interest" icon="envelope" href="https://info.polygon.technology/get-early-access?utm_source=docs&utm_medium=card&utm_campaign=oms_access">
    Share your use case and we'll reach out when deposit addresses and virtual accounts are available.
  </Card>
</Note>

Deposit Addresses are long-lived deposit configurations for crypto sources. Instead of the two-step quote/transaction flow, a deposit address monitors an onchain deposit address for incoming crypto. When funds arrive, OMS automatically creates and executes a transaction. Deposit Addresses do not expire: once provisioned, OMS keeps them active for reuse across deposits.

| Feature               | POST /quotes + POST /transactions    | Deposit Addresses                    |
| --------------------- | ------------------------------------ | ------------------------------------ |
| Amount known upfront? | Yes: locked at creation              | No: determined when funds arrive     |
| Sources               | OMS wallets                          | Crypto                               |
| Developer fees        | Percentage and/or fixed              | Percentage only                      |
| Lifecycle             | One-shot                             | Persistent: reusable across deposits |
| OMS fee visibility    | Inline on `source` and `destination` | `omsFeeSchedule` on address          |

Once provisioned, a deposit address's response includes `depositInstructions` with the blockchain address for the customer to send crypto to.

See the [Deposit Addresses](/payments/guides/deposit-addresses) guide for a full walkthrough.

***

## Virtual Accounts

Virtual Accounts give each customer a dedicated bank account number. When fiat arrives via the assigned bank rail, OMS automatically creates a payment that converts it to the specified crypto asset and delivers it to the customer's wallet.

| Feature         | Deposit Addresses              | Virtual Accounts                                  |
| --------------- | ------------------------------ | ------------------------------------------------- |
| Sources         | Crypto (onchain deposits)      | Bank only (fiat rail)                             |
| Deposit details | Blockchain address             | Dedicated bank account per customer               |
| Use case        | Reusable crypto deposit config | Persistent bank account number for fiat-to-crypto |
| ID prefix       | `da_`                          | `va_`                                             |

Virtual accounts behave like deposit addresses: persistent and reusable, and, like deposit addresses, currently provisioned through early access (see the note above).

See the [Virtual Accounts](/payments/guides/virtual-accounts) guide for a full walkthrough.

***

## Transaction Lifecycle

Every transaction moves through a predictable set of statuses. The top-level `status` field is designed for programmatic branching, while the optional `subStatus` field provides finer operational detail.

**Transaction statuses:**

| Status            | Meaning                                                                        |
| ----------------- | ------------------------------------------------------------------------------ |
| `processing`      | Transaction created. Funds being pulled, execution underway.                   |
| `cashPickupReady` | Cash off-ramp only: a pickup code has been issued and is ready for collection. |
| `completed`       | Funds delivered to destination.                                                |
| `failed`          | Async failure. The `error` object describes what went wrong.                   |

**Quote statuses:**

| Status     | Meaning                                                           |
| ---------- | ----------------------------------------------------------------- |
| `open`     | Quote created with locked pricing. Awaiting transaction creation. |
| `accepted` | Quote accepted. Transaction has been created from this quote.     |
| `expired`  | Pricing expired before transaction creation. Create a new quote.  |

**Auto-created transactions** from Deposit Addresses, Virtual Accounts, and Cash-Ins skip the Quote step: they go directly to `processing` since there is no developer review step.

**Sub-statuses** (`subStatus`) provide operational granularity as a free-form string (for example `fundsPulled` when source funds are pulled, or `cashPickupExpired`, which triggers a manual refund). Developers can safely ignore `subStatus` and branch only on `status`.

**Webhook events** fire on every meaningful state change, following the pattern `transaction.{type}.{event}`: for example, `transaction.cryptoToCrypto.completed`, `transaction.fiatToCrypto.failed`, or `transaction.cryptoToFiat.processing`. A `wallet.depositDetected` event fires when an unsolicited deposit arrives at a wallet address outside any OMS-initiated transaction. Payloads include the full transaction object, so polling is rarely necessary. Webhook subscriptions can be managed through the OMS Dashboard or via the API with the Webhooks endpoints (`POST`/`GET` `/webhooks` and `GET`/`PATCH`/`DELETE` `/webhooks/{id}`).

***

## Key Concepts

### Wallets

OMS provides both **custodial** and **non-custodial** wallets, so you can pick the custody model that fits your product.

* **Custodial wallets** are created and managed through the OMS API (`POST /customers/{id}/wallets`). OMS holds the keys and executes all operations as server-to-server API calls, no user signing required. Each wallet holds a single asset on one chain and has an on-chain address; read its balance with `GET /wallets/{id}/balance`.
* **Non-custodial wallets** put key control in the user's hands and are provided by Polygon's embedded wallet infrastructure. They sit alongside the OMS API and are useful for consumer apps that need user-signed transactions or self-custody. See the [Wallets section](/wallets/custodial-wallets) for the full custody options.

### Customers

Customers represent the end users in your application. Each customer owns one or more wallets, and can be associated with deposit addresses and virtual accounts (early access). Customers are assigned endorsements that determine which features and financial operations they can access. Endorsements include `basic` (standard operations), `cryptoCustody` (crypto custody and advanced features), and `usd` (USD stablecoin operations).

### Assets and Networks

Every side of a transaction has an **asset** (what kind of money) and a **network** (how it moves). The same field structure works for both crypto and fiat. All values are lowercase.

| Asset  | Network    | Meaning                                            |
| ------ | ---------- | -------------------------------------------------- |
| `usdc` | `polygon`  | USDC on Polygon Chain                              |
| `usdc` | `ethereum` | USDC on Ethereum mainnet                           |
| `usd`  | `ach`      | US dollars via ACH bank transfer                   |
| `usd`  | `wire`     | US dollars via domestic wire transfer              |
| `usd`  | `card`     | US dollars via debit card (coming soon)            |
| `usd`  | `cash`     | US dollars via physical cash (GreenDot / AllPoint) |

For the documented list of supported assets and networks, see the [Currencies and rails](/payments/core-concepts/currencies-and-rails) reference page.

### Fees and Pricing

Fee breakdowns are inline on `source` and `destination`: each side reports `amountGross` (before fees), `amountNet` (after fees), and a `feesDeducted` object with `total`, `developer`, `oms`, and `gas` components, all denominated in that side's asset. Top-level fields include `fixedAmountSide` (which side the caller specified), `rates` (exchange rate and effective rate), `sponsorGasCost`, and `sponsorGas`. The core equation is: `source.amountNet × rates.exchangeRate = destination.amountGross`. Developers who want to cover gas for their users can set `sponsorGas: true`, which moves gas costs to `sponsorGasCost` (an out-of-band developer cost).

### External Accounts

External accounts represent off-platform funding and payout endpoints (bank accounts, debit cards) that you reference by ID when creating a quote or transaction. They are currently provisioned through early access rather than created via the API.

External accounts use type-specific ID prefixes: `pmCard_` (debit cards), `cpIntlBank_` (international banks), `cpUsBank_` (US banks), `cpBcAddr_` (blockchain addresses).

***

## Get started

Get access, exchange your API key for a bearer token, then start calling endpoints.

<Steps>
  <Step title="Get API key access">
    The OMS API is in early access. API keys are granted on request, so request access and our team will reach out with sandbox and production credentials.

    <Card title="Request OMS access" icon="envelope" href="https://info.polygon.technology/get-early-access?utm_source=docs&utm_medium=card&utm_campaign=oms_access">
      Share your use case and we'll get you set up with API access.
    </Card>

    Once approved, open the OMS Dashboard, go to **API Keys**, and generate a key. A key is a name and secret pair; the secret is shown only once, so store it immediately.
  </Step>

  <Step title="Request an auth token">
    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 as `Authorization: Bearer {token}` on every other endpoint.

    <CodeGroup>
      ```bash Sandbox theme={null}
      curl -X POST https://sandbox-api.polygon.technology/v0.9/auth/token \
        -H "Content-Type: application/json" \
        -d '{
          "apiKey": "{api_key}",
          "apiSecret": "{api_secret}"
        }'
      ```

      ```bash Production theme={null}
      curl -X POST https://api.polygon.technology/v0.9/auth/token \
        -H "Content-Type: application/json" \
        -d '{
          "apiKey": "{api_key}",
          "apiSecret": "{api_secret}"
        }'
      ```
    </CodeGroup>

    The response returns an `accessToken` valid for 60 minutes. When a request returns `401`, the token has expired: request a new one and retry.
  </Step>
</Steps>

With a token in hand, create your first quote or browse the full endpoint reference:

<CardGroup cols={2}>
  <Card title="Create a payment quote" icon="receipt" href="/api-reference/quote/create-a-payment-quote">
    Lock pricing with `POST /quotes`, the first step of the two-step transaction flow.
  </Card>

  <Card title="Get bearer token reference" icon="key" href="/api-reference/auth/get-bearer-token">
    Full request and response reference for `POST /auth/token`.
  </Card>
</CardGroup>

For a step-by-step walkthrough from zero to your first transaction, see the [Get started guide](/payments/get-started).
