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:| 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
- Create a Quote.
POST /quoteswith source, destination, and amount. OMS locks pricing and returns the quote inopenstatus with a full fee breakdown. - Create a Transaction. Review the pricing, then call
POST /transactionswith the quote ID. OMS begins execution and the transaction moves toprocessing. - Track. Poll
GET /transactions/{id}or listen for webhook events.
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.- Create.
POST /cash-inswith a destination wallet, location, and developer fees. OMS returns a deposit code valid for 1 hour. - Deposit. The customer presents the code at the location and deposits any amount of cash.
- Convert. OMS receives the deposit, applies fees (OMS + developer), converts to the destination asset, and delivers crypto. The cash-in status moves to
completedand a transaction record is auto-created.
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.
| 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 |
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.| 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_ |
Transaction Lifecycle
Every transaction moves through a predictable set of statuses. The top-levelstatus 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. |
| 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. |
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 withGET /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 includebasic (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) |
Fees and Pricing
Fee breakdowns are inline onsource 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).