Skip to main content
Access is by request. The Open Money Stack API is in early access. API keys are granted on request, so fill out the early-access form and our team will reach out with sandbox and production credentials.

Request OMS access

Share your use case and we’ll get you set up with API access.

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:
MechanismWhat It DoesWhen to Use
TransactionsSingle 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 AddressesPersistent 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 AccountsDedicated 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. See the Fiat to Crypto and 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 guide for a full walkthrough.

Deposit Addresses

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.

Register interest

Share your use case and we’ll reach out when deposit addresses and virtual accounts are available.
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.
FeaturePOST /quotes + POST /transactionsDeposit Addresses
Amount known upfront?Yes: locked at creationNo: determined when funds arrive
SourcesOMS walletsCrypto
Developer feesPercentage and/or fixedPercentage only
LifecycleOne-shotPersistent: reusable across deposits
OMS fee visibilityInline on source and destinationomsFeeSchedule 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 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.
FeatureDeposit AddressesVirtual Accounts
SourcesCrypto (onchain deposits)Bank only (fiat rail)
Deposit detailsBlockchain addressDedicated bank account per customer
Use caseReusable crypto deposit configPersistent bank account number for fiat-to-crypto
ID prefixda_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 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:
StatusMeaning
processingTransaction created. Funds being pulled, execution underway.
cashPickupReadyCash off-ramp only: a pickup code has been issued and is ready for collection.
completedFunds delivered to destination.
failedAsync failure. The error object describes what went wrong.
Quote statuses:
StatusMeaning
openQuote created with locked pricing. Awaiting transaction creation.
acceptedQuote accepted. Transaction has been created from this quote.
expiredPricing 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 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.
AssetNetworkMeaning
usdcpolygonUSDC on Polygon Chain
usdcethereumUSDC on Ethereum mainnet
usdachUS dollars via ACH bank transfer
usdwireUS dollars via domestic wire transfer
usdcardUS dollars via debit card (coming soon)
usdcashUS dollars via physical cash (GreenDot / AllPoint)
For the documented list of supported assets and networks, see the 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).