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.
Deposit address provisioning and management is not yet available in the OMS API. To be notified when it launches, register your interest.

Register interest

Share your use case and we’ll reach out when deposit address provisioning and management is available.
The flow below shows the intended pattern end to end. The create and manage steps (Step 1 and the Managing Deposit Addresses section) are not yet available: there are no endpoints to create, update, list, or delete a deposit address today. The transaction reads referenced in Step 3 (GET /transactions/{id} and GET /customers/{id}/transactions) are available now.
Deposit address flow
1AppOMSPOST /customers/{id}/deposit-addresses
2OMSApp{depositInstructions: {address: “0x…“}}
3AppSenderShare onchain address
4SenderPolygonSend USDC to address
5PolygonOMSDeposit detected
6OMSAuto-create and execute transaction
7OMSAppWebhook: transaction.completed

Prerequisites

Before creating a Deposit Address, you need:
  1. A customer with a cst_ ID and the cryptoCustody endorsement active.
  2. A crypto destination: a WalletTarget object with one of wallet (OMS custodial wallet, wlt_), blockchainAddress (external on-chain address), or externalAccount (saved on-chain address, cpBcAddr_).
  3. Webhook subscriptions configured in the OMS Dashboard for transaction.cryptoToCrypto.* events.

Step 1: Create the Deposit Address

Creating a deposit address is not yet available in the OMS API. The request and response below describe the intended shape so you can plan your integration.
A Deposit Address is a persistent, reusable configuration that tells OMS: “When crypto arrives at this address, convert and deliver it to a destination.” There is no amount specified upfront, the amount is determined when funds actually arrive. Both sides are crypto: sources accept usdc or usdt on polygon or ethereum, and destinations are specified via a WalletTarget object (the same shape used elsewhere for crypto delivery).

Request

POST /v0.9/customers/cst_01H9Xa.../deposit-addresses
Authorization: Bearer {api_key}
Idempotency-Key: da-alice-usdc-polygon-001
Content-Type: application/json
{
  "source": {
    "asset": "usdc",
    "network": "polygon"
  },
  "destination": {
    "wallet": {
      "wallet": "wlt_01H9Xb..."
    },
    "asset": "usdc",
    "network": "polygon"
  },
  "developerFees": [
    { "type": "percentage", "rate": "0.015", "wallet": "wlt_01H9Xf..." }
  ],
  "label": "Alice deposit address",
  "metadata": {
    "purpose": "user_deposit"
  }
}
Required fields:
  • source.asset: The crypto asset to accept (usdc or usdt).
  • source.network: The blockchain to monitor (polygon or ethereum).
  • destination.asset: The crypto asset to deliver (usdc or usdt).
  • destination.network: The destination rail (polygon or ethereum).
  • A destination.wallet object with one of: wallet, blockchainAddress, or externalAccount.
Optional fields:
  • developerFees: Array of fee entries. Each entry has type (percentage), rate (as a string, e.g., "0.015"), and optional wallet (OMS wallet receiving the fee). Percentage-only because the deposit amount is unknown until funds arrive. If omitted, no developer fees are collected.
  • label: A human-readable name for this address.
  • metadata: Up to 20 arbitrary key-value pairs.

Response, 201 Created

{
  "id": "da_01H9Xy...",
  "object": "depositAddress",
  "customerId": "cst_01H9Xa...",
  "status": "active",
  "transactionType": "cryptoToCrypto",
  "source": {
    "asset": "usdc",
    "network": "polygon",
    "depositInstructions": {
      "address": "0xABC123...",
      "asset": "usdc",
      "network": "polygon"
    }
  },
  "destination": {
    "wallet": {
      "wallet": "wlt_01H9Xb..."
    },
    "asset": "usdc",
    "network": "polygon"
  },
  "developerFees": [
    { "type": "percentage", "rate": "0.015", "wallet": "wlt_01H9Xf..." }
  ],
  "omsFeeSchedule": {
    "feeCurrency": "usd",
    "entries": [
      { "type": "percentage", "rate": "0.02" },
      { "type": "fixed", "amount": "3.00" }
    ]
  },
  "label": "Alice deposit address",
  "metadata": {
    "purpose": "user_deposit"
  },
  "createdAt": "2024-01-15T10:00:00Z",
  "updatedAt": "2024-01-15T10:00:00Z"
}
Key fields in the response:
  • id: The Deposit Address ID (prefix da_). Use this for GET, PATCH, and DELETE operations.
  • status: Starts as active. Can be changed to paused or deleted later.
  • transactionType: Always cryptoToCrypto for the destinations defined in the request schema.
  • source.depositInstructions: The blockchain address your customer should send funds to. This is server-generated and unique to this Deposit Address.
  • omsFeeSchedule: Shows how OMS fees will be calculated on auto-created transactions. Since there is no Quote step for Deposit Addresses, this gives you visibility into the fee structure upfront.
What to do with the response: Surface source.depositInstructions.address to your customer as the address they should send crypto to. Store da_01H9Xy... in your system for tracking.

Test in sandbox

Once a deposit address is provisioned for you, you can simulate an inbound deposit in sandbox instead of sending real crypto. Call POST /deposit-addresses/{depositAddressId}/simulate with an amount in cents. OMS returns a synthetic transactionHash and fires the same transaction.cryptoToCrypto.* webhooks as a real deposit. This endpoint returns 404 in production.
curl -X POST https://sandbox-api.polygon.technology/v0.9/deposit-addresses/da_01H9Xy.../simulate \
  -H "Authorization: Bearer {api_key}" \
  -H "Content-Type: application/json" \
  -d '{ "amount": { "value": "25000" } }'

Step 2: Customer Sends Funds

The customer sends crypto to the depositInstructions.address from any external wallet. There is nothing to do on the API side at this point, OMS monitors the blockchain for incoming deposits. For this example, the customer sends 250.00 USDC on Polygon to 0xABC123....

Step 3: OMS Detects the Deposit and Auto-Creates a Transaction

When OMS detects the onchain deposit, it automatically creates a Transaction in processing status. This transaction skips the Quote step entirely, there is no Quote object, and quote is null.

Webhook, transaction.cryptoToCrypto.processing

OMS fires this webhook as soon as the transaction is created:
{
  "id": "whk_evt_01H9Xw...",
  "event": "transaction.cryptoToCrypto.processing",
  "createdAt": "2024-01-15T14:30:00Z",
  "data": {
    "id": "txn_01H9Xd...",
    "object": "transaction",
    "type": "cryptoToCrypto",
    "status": "processing",
    "subStatus": "processing.fundsDetected",
    "customerId": "cst_01H9Xa...",
    "quoteId": null,
    "depositAddress": "da_01H9Xy...",
    "virtualAccount": null,
    "cashIn": null,
    "source": {
      "asset": "usdc",
      "network": "polygon",
      "amountGross": "250.00",
      "amountNet": "246.25",
      "feesDeducted": {
        "total": "3.75",
        "developer": "3.75",
        "oms": "0.00",
        "gas": "0.00"
      },
      "txHash": "0x8a3b7c...d4e5f6"
    },
    "destination": {
      "wallet": {
        "wallet": "wlt_01H9Xb..."
      },
      "asset": "usdc",
      "network": "polygon",
      "amountGross": "246.25",
      "amountNet": "246.25",
      "feesDeducted": {
        "total": "0.00",
        "developer": "0.00",
        "oms": "0.00",
        "gas": "0.00"
      }
    },
    "fixedAmountSide": "source",
    "sponsorGasCost": "0.00",
    "sponsorGas": false,
    "rates": {
      "exchangeRate": "1.0000",
      "effectiveRate": "0.9850"
    },
    "estimatedArrival": null,
    "error": null,
    "metadata": {
      "purpose": "user_deposit"
    },
    "createdAt": "2024-01-15T14:30:00Z",
    "updatedAt": "2024-01-15T14:30:00Z"
  }
}
What to notice:
  • quote: null: Auto-created transactions skip the Quote step.
  • depositAddress: "da_01H9Xy...": Links this transaction back to the originating Deposit Address.
  • source.txHash: The onchain transaction hash of the incoming deposit.
  • source.amountGross: "250.00": The actual amount deposited, now known.
  • source.amountNet: "246.25": After deducting the 3.75 developer fee.
  • destination.amountGross: "246.25": Amount to be delivered.
  • destination.amountNet: "246.25": Same as gross (no additional fees on destination).
  • source.feesDeducted.developer: "3.75": Your 1.5% fee on the 250.00 USDC deposit.
  • fixedAmountSide: "source": The source amount is fixed; destination is calculated from it.
  • The metadata from the Deposit Address is carried over to the transaction.

Polling Alternative

If you prefer polling over webhooks, you can retrieve the transaction directly:
GET /v0.9/transactions/txn_01H9Xd...
Authorization: Bearer {api_key}
Or list all transactions for the customer:
GET /v0.9/customers/cst_01H9Xa.../transactions
Authorization: Bearer {api_key}

Step 4: Transaction Completes

OMS processes the conversion (in this crypto-to-crypto example, no exchange is needed since source and destination are both USDC on Polygon) and delivers funds to the destination wallet.

Webhook, transaction.cryptoToCrypto.completed

{
  "id": "whk_evt_01H9Xw2...",
  "event": "transaction.cryptoToCrypto.completed",
  "createdAt": "2024-01-15T14:30:45Z",
  "data": {
    "id": "txn_01H9Xd...",
    "object": "transaction",
    "type": "cryptoToCrypto",
    "status": "completed",
    "subStatus": null,
    "customerId": "cst_01H9Xa...",
    "quoteId": null,
    "depositAddress": "da_01H9Xy...",
    "virtualAccount": null,
    "cashIn": null,
    "source": {
      "asset": "usdc",
      "network": "polygon",
      "amountGross": "250.00",
      "amountNet": "246.25",
      "feesDeducted": {
        "total": "3.75",
        "developer": "3.75",
        "oms": "0.00",
        "gas": "0.00"
      },
      "txHash": "0x8a3b7c...d4e5f6"
    },
    "destination": {
      "wallet": {
        "wallet": "wlt_01H9Xb..."
      },
      "asset": "usdc",
      "network": "polygon",
      "amountGross": "246.25",
      "amountNet": "246.25",
      "feesDeducted": {
        "total": "0.00",
        "developer": "0.00",
        "oms": "0.00",
        "gas": "0.00"
      }
    },
    "fixedAmountSide": "source",
    "sponsorGasCost": "0.00",
    "sponsorGas": false,
    "rates": {
      "exchangeRate": "1.0000",
      "effectiveRate": "0.9850"
    },
    "estimatedArrival": null,
    "error": null,
    "metadata": {
      "purpose": "user_deposit"
    },
    "createdAt": "2024-01-15T14:30:00Z",
    "updatedAt": "2024-01-15T14:30:45Z"
  }
}
What changed from the processing webhook:
  • status is now completed and subStatus is null (sub-statuses only apply to the cash pickup flow).
  • updatedAt reflects the completion time.
At this point the flow is done. The customer’s 250 USDC has been received, your 3.75 USDC developer fee has been collected to wlt_01H9Xf..., and 246.25 USDC has been delivered to the destination wallet wlt_01H9Xb....

Step 5 (Reuse): More Deposits

The Deposit Address remains active and continues monitoring 0xABC123.... Every subsequent deposit to that address triggers the same flow, a new auto-created transaction with a new txn_ ID, the same depositAddress, and fees calculated from the same configuration. There is no limit on the number of transactions a single Deposit Address can produce.

Managing Deposit Addresses

The management operations below (pause, resume, change destination, delete, and list) are not yet available in the OMS API. They describe the intended pattern.

Pause a Deposit Address

Pausing stops OMS from processing new deposits. Any in-flight transactions continue to completion.
PATCH /v0.9/deposit-addresses/da_01H9Xy...
Authorization: Bearer {api_key}
Content-Type: application/json
{
  "status": "paused"
}
Response returns the updated Deposit Address with "status": "paused".

Resume a Deposit Address

{
  "status": "active"
}

Change the Destination

You can redirect future deposits to a different wallet or external account without creating a new Deposit Address. The source (and its deposit instructions) remain the same.
PATCH /v0.9/deposit-addresses/da_01H9Xy...
Authorization: Bearer {api_key}
Content-Type: application/json
{
  "destination": {
    "wallet": {
      "wallet": "wlt_01H9Xc..."
    }
  }
}
Note: You cannot change the source. If you need a different source asset or network, create a new Deposit Address.

Delete a Deposit Address

Soft-deletes the address. OMS stops monitoring for deposits.
DELETE /v0.9/deposit-addresses/da_01H9Xy...
Authorization: Bearer {api_key}
Response: 204 No Content.

List Deposit Addresses

GET /v0.9/customers/cst_01H9Xa.../deposit-addresses?status=active
Authorization: Bearer {api_key}
{
  "data": [
    {
      "id": "da_01H9Xy...",
      "status": "active",
      "transactionType": "cryptoToCrypto",
      "...": "..."
    },
  ],
  "hasMore": false,
  "cursor": null
}

Failure Handling

If a transaction fails after the deposit is detected, OMS fires a transaction.cryptoToCrypto.failed webhook with an error object:
{
  "id": "whk_evt_01H9Xw3...",
  "event": "transaction.cryptoToCrypto.failed",
  "createdAt": "2024-01-15T14:31:00Z",
  "data": {
    "id": "txn_01H9Xd...",
    "type": "cryptoToCrypto",
    "status": "failed",
    "subStatus": "failed.onChainRevert",
    "error": {
      "code": "onChainRevert",
      "message": "Unable to deliver funds to destination wallet"
    },
    "...rest of transaction object..."
  }
}
If OMS successfully refunds the crypto back to the sender, you’ll receive a transaction.cryptoToCrypto.refundCompleted webhook. If the refund itself fails, you’ll receive transaction.cryptoToCrypto.refundFailed.

Compliance Review

In some cases, a transaction may be held for compliance review. This fires a developer-only webhook (not forwarded to customers):
{
  "id": "whk_evt_01H9Xw4...",
  "event": "transaction.cryptoToCrypto.underReview",
  "createdAt": "2024-01-15T14:30:10Z",
  "data": {
    "id": "txn_01H9Xd...",
    "type": "cryptoToCrypto",
    "status": "processing",
    "subStatus": "processing.underReview",
    "...rest of transaction object..."
  }
}
The transaction remains in processing status during review. Once resolved, it proceeds to completed or failed with the corresponding webhook.

Summary: Webhook Events for Deposit Address Transactions

EventWhen
transaction.cryptoToCrypto.processingDeposit detected, transaction created
transaction.cryptoToCrypto.completedFunds delivered to destination
transaction.cryptoToCrypto.failedDelivery failed
transaction.cryptoToCrypto.refundCompletedRefund sent back to sender
transaction.cryptoToCrypto.refundFailedRefund attempt failed
transaction.cryptoToCrypto.underReviewUnder compliance review (developer-only)

Key Differences from Quote → Transaction Flow

Deposit Addresses differ from the standard Quote → Transaction flow in several ways:
  • No amount upfront. The Quote flow requires a known amount to generate pricing. Deposit Addresses handle “send any amount” scenarios.
  • No Quote object. Auto-created transactions have quote: null. Pricing is calculated when the deposit arrives.
  • Fee visibility via omsFeeSchedule. Since there’s no Quote with exact pricing, the Deposit Address response includes omsFeeSchedule so developers know the fee structure in advance.
  • Percentage-only developer fees. Fixed USD fees aren’t supported because the deposit amount is unknown until arrival.
  • Reusable. A single Deposit Address produces unlimited transactions over its lifetime. Quotes are one-time-use.
  • Transactions start at processing. There’s no openaccepted step. The transaction goes straight to processing when the deposit is detected.