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

# Deposit addresses

> How to accept crypto at a persistent on-chain address that auto-converts incoming funds and pays out to a bank account.

<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 {token}` 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>

<Note>
  Deposit addresses must be enabled for your project before `POST /deposit-addresses` succeeds, and the customer must be provisioned for them. Contact us to enable deposit addresses for your project.

  <Card title="Contact us" icon="envelope" href="https://info.polygon.technology/get-early-access?utm_source=docs&utm_medium=card&utm_campaign=oms_access">
    Share your on-ramp use case and we'll enable deposit addresses for your project.
  </Card>
</Note>

A deposit address is a reusable on-chain address assigned to a customer. When crypto arrives at the address, OMS automatically creates and executes a `cryptoToFiatAccount` transaction that converts it and pays out to the customer's registered bank account. There is no quote step and no amount specified upfront: the amount is whatever the sender deposits.

<div style={{border:"1px solid #C8CFE1",borderRadius:"12px",overflow:"hidden",marginBottom:"24px"}}>
  <div style={{background:"linear-gradient(180deg,#EAE4F5 0%,#F6F3FB 100%)",borderBottom:"1px solid #D5C4F2",padding:"10px 16px",fontSize:"11px",fontWeight:"700",color:"#670DE5",letterSpacing:"0.06em",textTransform:"uppercase"}}>Deposit address flow</div>

  <div style={{borderBottom:"1px solid #EEF0F9",padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>1</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>App</span>
    <span style={{color:"#670DE5",fontWeight:"700"}}>→</span>
    <span style={{background:"#EAE4F5",color:"#670DE5",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"700",whiteSpace:"nowrap"}}>OMS</span>
    <span style={{fontSize:"13px",color:"#141635"}}>Create the deposit address with POST /deposit-addresses</span>
  </div>

  <div style={{borderBottom:"1px solid #EEF0F9",padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>2</span>
    <span style={{background:"#EAE4F5",color:"#670DE5",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"700",whiteSpace:"nowrap"}}>OMS</span>
    <span style={{color:"#670DE5",fontWeight:"700"}}>→</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>App</span>
    <span style={{fontSize:"13px",color:"#141635"}}>Provisioning completes and populates depositInstructions</span>
  </div>

  <div style={{borderBottom:"1px solid #EEF0F9",padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>3</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>App</span>
    <span style={{color:"#670DE5",fontWeight:"700"}}>→</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>Sender</span>
    <span style={{fontSize:"13px",color:"#141635"}}>Share the on-chain inlet address</span>
  </div>

  <div style={{borderBottom:"1px solid #EEF0F9",padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>4</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>Sender</span>
    <span style={{color:"#670DE5",fontWeight:"700"}}>→</span>
    <span style={{background:"#EAE4F5",color:"#670DE5",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"700",whiteSpace:"nowrap"}}>OMS</span>
    <span style={{fontSize:"13px",color:"#141635"}}>Send USDC or USDT to the address</span>
  </div>

  <div style={{borderBottom:"1px solid #EEF0F9",padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>5</span>
    <span style={{background:"#EAE4F5",color:"#670DE5",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"700",whiteSpace:"nowrap"}}>OMS</span>
    <span style={{fontSize:"13px",color:"#141635",marginLeft:"4px"}}>Detect deposit, auto-create and execute the cryptoToFiatAccount transaction</span>
  </div>

  <div style={{padding:"9px 16px",display:"flex",alignItems:"center",gap:"10px"}}>
    <span style={{color:"#929EBA",fontSize:"11px",fontWeight:"700",minWidth:"16px",textAlign:"right"}}>6</span>
    <span style={{background:"#EAE4F5",color:"#670DE5",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"700",whiteSpace:"nowrap"}}>OMS</span>
    <span style={{color:"#670DE5",fontWeight:"700"}}>→</span>
    <span style={{background:"#EEF0F9",color:"#48526F",padding:"2px 8px",borderRadius:"4px",fontSize:"11px",fontWeight:"600",whiteSpace:"nowrap"}}>App</span>
    <span style={{fontFamily:"'Geist Mono',ui-monospace,monospace",fontSize:"12px",color:"#141635"}}>Webhook: transaction.cryptoToFiat.completed</span>
  </div>
</div>

## Prerequisites

Before you can create a deposit address, you need:

1. **A customer** with a `cst_` ID and the `cryptoCustody` endorsement active.
2. **A registered bank external account** owned by that customer to receive the payout. Register one with `POST /external-accounts` (`type` of `bankUs`, `bankIban`, or `bankCanada`); the account's `ext_` ID goes in the deposit address's `destination`.
3. **Deposit addresses enabled** for your project and the customer provisioned for them (contact us).
4. **A webhook subscription** covering the `transaction.cryptoToFiat.*` events (and, optionally, the `depositAddress.*` lifecycle events) so you learn when the auto-created transaction is delivered. Register one with `POST /webhooks` (body `{ url, events }`) or in the OMS Dashboard. See the [transaction lifecycle](/payments/core-concepts/transaction-lifecycle) for the delivery model.

## Create a deposit address

Create the address with `POST /deposit-addresses`. You name the customer, the inbound asset and network the address watches, and the registered bank external account that receives the converted funds.

<CodeGroup>
  ```bash Sandbox theme={null}
  curl -X POST https://sandbox-api.polygon.technology/v0.10/deposit-addresses \
    -H "Authorization: Bearer {token}" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: da-alice-usdc-001" \
    -d '{
      "customerId": "cst_01H9Xa...",
      "expectedSourceAsset": "usdc",
      "expectedSourceNetwork": "base",
      "destination": {
        "type": "bankUs",
        "details": {
          "id": "ext_bankUs_01H9Xf...",
          "asset": "usd",
          "network": "ach",
          "accountHolder": "customer"
        }
      },
      "label": "Alice USDC inbound"
    }'
  ```

  ```bash Production theme={null}
  curl -X POST https://api.polygon.technology/v0.10/deposit-addresses \
    -H "Authorization: Bearer {token}" \
    -H "Content-Type: application/json" \
    -H "Idempotency-Key: da-alice-usdc-001" \
    -d '{
      "customerId": "cst_01H9Xa...",
      "expectedSourceAsset": "usdc",
      "expectedSourceNetwork": "base",
      "destination": {
        "type": "bankUs",
        "details": {
          "id": "ext_bankUs_01H9Xf...",
          "asset": "usd",
          "network": "ach",
          "accountHolder": "customer"
        }
      },
      "label": "Alice USDC inbound"
    }'
  ```
</CodeGroup>

* `customerId`: the customer who owns the address. Required.
* `expectedSourceAsset`: the inbound stablecoin the address expects, `usdc` or `usdt` (lowercase). Required.
* `expectedSourceNetwork`: the network the address watches for deposits, for example `base`, `ethereum`, or `solana`. Required.
* `destination`: the registered bank external account that receives the payout. Required. `type` is `bankUs`, `bankIban`, or `bankCanada`, and `details` carries the account's `id` (for example `ext_bankUs_...`), `asset`, `network`, and `accountHolder`. `accountHolder` must be `customer`. OMS validates the `details` against the resolved external account.
* `sponsorGas`: when `true`, OMS absorbs the on-chain gas cost of the destination delivery. Optional, defaults to `true`; only `true` is currently supported.
* `label` and `metadata`: an optional display label and an optional string-to-string map for your own references.

The `201` response returns the deposit address with a `da_` ID:

```json theme={null}
{
  "id": "da_01H9Xy...",
  "object": "depositAddress",
  "customerId": "cst_01H9Xa...",
  "status": "pending",
  "statusReason": null,
  "failureReason": null,
  "expectedSourceAsset": "usdc",
  "expectedSourceNetwork": "base",
  "destination": {
    "type": "bankUs",
    "category": "fiatAccount",
    "details": {
      "id": "ext_bankUs_01H9Xf...",
      "asset": "usd",
      "network": "ach",
      "accountNumberLast4": "4321",
      "routingNumber": "021000021",
      "accountType": "checking",
      "bankName": "Example Bank"
    }
  },
  "depositInstructions": null,
  "transactionType": "cryptoToFiat",
  "label": "Alice USDC inbound",
  "metadata": {},
  "createdAt": "2026-01-15T14:30:00Z",
  "updatedAt": "2026-01-15T14:30:00Z"
}
```

`depositInstructions` is `null` in the `201` response: OMS provisions the on-chain inlet address asynchronously. `transactionType` is always `cryptoToFiat`; the transactions the address produces report `sourceToDestination: cryptoToFiatAccount`.

## Wait for provisioning, then share the address

Poll `GET /deposit-addresses/{depositAddressId}` (or re-fetch the address before you display it) until `status` is `active` and `depositInstructions` is populated:

```
GET /v0.10/deposit-addresses/da_01H9Xy...
Authorization: Bearer {token}
```

Once the address is `active`, `depositInstructions` carries the inlet address to display to your customer:

```json theme={null}
{
  "address": "0xABC123...",
  "asset": "usdc",
  "network": "base",
  "expiresAt": null
}
```

| Field       | Meaning                                                                                    |
| ----------- | ------------------------------------------------------------------------------------------ |
| `address`   | The OMS-owned on-chain inlet address for this deposit address. Give this to your customer. |
| `asset`     | The stablecoin the address accepts. Same value as `expectedSourceAsset`.                   |
| `network`   | The chain the address accepts funds on. Same value as `expectedSourceNetwork`.             |
| `expiresAt` | Reserved for a future provider-imposed inlet expiry. Null today.                           |

Crypto sent to `address` is converted and paid out to the configured bank external account.

<Note>
  Deposit-address webhooks are live. The address itself fires lifecycle events (`depositAddress.created`, `depositAddress.active`, `depositAddress.paused`, `depositAddress.resumed`, `depositAddress.frozen`, `depositAddress.closed`, `depositAddress.deleted`, `depositAddress.failed`); each inbound deposit fires `deposit_address.crypto_deposit.*` events; and the payout leg fires `deposit_address.ach_payout.*`, `deposit_address.wire_payout.*`, or `deposit_address.intl_wire_payout.*` events. Subscribe to `depositAddress.active` to learn when the inlet address is ready instead of polling. See [Webhook events](/api-reference/webhook-events) for the full catalog.
</Note>

## Test in sandbox

In sandbox, simulate an inbound transfer to exercise the auto-created transaction path without moving real funds. Call `POST /deposit-addresses/{depositAddressId}/simulate` with the amount to simulate. The currency and network are resolved server-side from the deposit address. This endpoint returns `404` in production.

```bash theme={null}
curl -X POST https://sandbox-api.polygon.technology/v0.10/deposit-addresses/da_01H9Xy.../simulate \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{ "amount": { "value": "25000" } }'
```

The `value` is the stablecoin amount in cents (greater than 0 and at most 100000). The response echoes the simulated deposit and returns a synthetic `transactionHash` you can correlate against the webhook:

```json theme={null}
{
  "depositAddressId": "da_01H9Xy...",
  "amount": { "value": "25000", "currency": "USDC" },
  "network": { "...": "..." },
  "transactionHash": "0x...",
  "status": "submitted",
  "submittedAt": "2026-01-15T10:32:00Z"
}
```

<Tip>
  The simulated inbound funds create the same auto-created transaction that a real deposit would, so this is the way to build and verify your webhook and reconciliation handling before going live.
</Tip>

## What OMS creates when funds arrive

When OMS detects the deposit (real or simulated), it creates a transaction in `processing` status and fires the `transaction.cryptoToFiat.processing` event, with the full transaction under `payload`. The transaction skips the quote step, so its `precursor` is typed `depositAddress` and carries the `depositAddressId`. Pricing is calculated at the moment funds arrive and lives in the top-level `pricing` object.

The transaction's `sourceToDestination` is `cryptoToFiatAccount`: the source is the sender's on-chain transfer and the destination is the deposit address's configured bank external account.

```json theme={null}
{
  "eventId": "evt_01H9Xw...",
  "eventName": "transaction.cryptoToFiat.processing",
  "resourceType": "transaction_crypto_to_fiat",
  "resourceId": "txn_01H9Xd...",
  "occurredAt": "2026-01-15T14:30:00Z",
  "payload": {
    "id": "txn_01H9Xd...",
    "object": "transaction",
    "status": "processing",
    "subStatus": "processing.fundsPulled",
    "customerId": "cst_01H9Xa...",
    "sourceToDestination": "cryptoToFiatAccount",
    "precursor": {
      "type": "depositAddress",
      "details": {
        "depositAddressId": "da_01H9Xy...",
        "depositInstructions": {
          "address": "0xABC123...",
          "asset": "usdc",
          "network": "base"
        }
      }
    },
    "source": {
      "type": "walletExternal",
      "category": "crypto",
      "details": {
        "blockchainAddress": "0xSENDER...",
        "asset": "usdc",
        "network": "base",
        "txHash": "0x8a3b7c...d4e5f6"
      }
    },
    "destination": {
      "type": "bankUs",
      "category": "fiatAccount",
      "details": {
        "id": "ext_bankUs_01H9Xf...",
        "asset": "usd",
        "network": "ach",
        "accountNumberLast4": "4321",
        "routingNumber": "021000021",
        "accountType": "checking",
        "bankName": "Example Bank"
      }
    },
    "pricing": {
      "source": {
        "asset": "usdc",
        "amountGross": "250.00",
        "amountNet": "246.25",
        "feesDeducted": { "total": "3.75", "developer": "3.75", "oms": "0.00", "gas": "0.00" }
      },
      "destination": {
        "asset": "usd",
        "amountGross": "246.25",
        "amountNet": "246.25"
      },
      "pair": "usdc/usd",
      "exchangeRate": "1.0",
      "effectiveRate": "0.985",
      "fixedAmountSide": "source",
      "sponsorGas": true,
      "sponsorGasCost": "0"
    },
    "estimatedArrival": null,
    "error": null,
    "createdAt": "2026-01-15T14:30:00Z",
    "updatedAt": "2026-01-15T14:30:00Z"
  }
}
```

**What to notice:**

* `precursor.type` is `depositAddress`, and `precursor.details.depositAddressId` links the transaction back to the originating deposit address.
* `source.details.txHash` is the on-chain hash of the incoming deposit.
* `destination` is the bank external account you configured on the deposit address.
* `pricing.source.amountGross` is the amount actually deposited, now known.
* `pricing.source.feesDeducted.developer` is your fee, computed as a percentage of the deposit.
* `fixedAmountSide` is `source`: the deposited amount is fixed and the destination payout is calculated from it.

## Track the transaction

Branch on the transaction `status`. `processing` means the deposit was detected and execution is underway; `completed` means funds were delivered; `failed` is a terminal failure with an `error` object. See the [transaction lifecycle](/payments/core-concepts/transaction-lifecycle) for the full status model and sub-statuses.

Prefer webhooks over polling: OMS fires `transaction.cryptoToFiat.processing`, `transaction.cryptoToFiat.completed`, and `transaction.cryptoToFiat.failed` for the auto-created transaction, and each delivery carries the full transaction object under `payload`, so your handler branches on the event name or `payload.status`. If you do poll, read the transaction directly:

```
GET /v0.10/transactions/txn_01H9Xd...
Authorization: Bearer {token}
```

Or scope a listing to the customer:

```
GET /v0.10/transactions?customerId=cst_01H9Xa...
Authorization: Bearer {token}
```

## Reuse

A deposit address is persistent. While it is `active` it keeps monitoring its inlet address, so every subsequent deposit triggers the same flow: a new transaction with a new `txn_` ID, the same `depositAddressId`, and pricing computed from the same configuration. There is no limit on the number of transactions a single deposit address can produce.

## Status lifecycle

| Status                   | Meaning                                                                                                           |
| ------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `pending`                | Created; OMS is provisioning the inlet address. `depositInstructions` is still null.                              |
| `active`                 | The inlet address is live. Deposits convert and pay out to the destination.                                       |
| `frozen`                 | Deposits are suspended. `statusReason` explains why.                                                              |
| `closed`                 | The address is permanently closed and no longer monitors its inlet address.                                       |
| `failed`                 | Provisioning failed; `failureReason` identifies the category. Create a new deposit address.                       |
| `inactiveActionRequired` | The destination external account is no longer usable. Re-point `destination` with `PATCH` to recover to `active`. |

## Held deposits

A deposit can arrive in a state the transaction cannot settle from. Instead of failing, the auto-created transaction moves to `awaitingAction` with a typed `hold` that explains what is blocking it and, when a deadline applies, how long you have to resolve it. A deposit held for sender attribution also fires the `deposit_address.crypto_deposit.needs_attribution` event:

| `hold.type`              | Cause                                                                                                                                                                                             | Resolution                                                                                                                                                                                                                                   |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `senderAttribution`      | The deposit came from an on-chain address OMS cannot attribute to a known sender. The hold carries the `txHash` and `matchableExternalAccountCriteria` (the address and network family to match). | Register a `walletExternal` external account matching the criteria. The registration's create response lists the released transactions in `resolvedTransactions`, and each moves back to `processing` once the provider confirms settlement. |
| `depositAddressFrozen`   | The deposit arrived while the deposit address was `frozen`.                                                                                                                                       | Clears when the freeze lifts; no developer action.                                                                                                                                                                                           |
| `depositAddressInactive` | The destination external account became unusable (see `hold.cause` for the account, its status, and the reason).                                                                                  | Re-point the deposit address `destination` to a healthy external account with `PATCH`.                                                                                                                                                       |

Unresolved holds fail at their deadline with a matching terminal sub-status (`failed.attributionTimeout`, `failed.depositAddressFrozenTimeout`, `failed.depositAddressInactiveTimeout`). Branch on `status`; use `subStatus` and `hold` for operational detail.

## Manage deposit addresses

### List

`GET /deposit-addresses` spans every customer in your organization. Filter with `customerId` and `status`, both optional. Paginate with `limit`, `startingAfter`, and `endingBefore`:

```
GET /v0.10/deposit-addresses?customerId=cst_01H9Xa...&status=active&limit=20
Authorization: Bearer {token}
```

The response is a list envelope: `{ object, data, hasMore, nextCursor, previousCursor }`. Pass `nextCursor` as `startingAfter` to fetch the next page, or `previousCursor` as `endingBefore` to page backward; `hasMore` signals whether more rows exist in the direction of travel.

### Update

`PATCH /deposit-addresses/{depositAddressId}` accepts `destination`, `label`, and `metadata`; `sponsorGas` is also accepted, but only `true` is supported. Any other key in the body is rejected with `400`.

```bash theme={null}
curl -X PATCH https://sandbox-api.polygon.technology/v0.10/deposit-addresses/da_01H9Xy... \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: da-repoint-001" \
  -d '{
    "destination": {
      "type": "bankUs",
      "details": {
        "id": "ext_bankUs_01H9Xg...",
        "asset": "usd",
        "network": "ach",
        "accountHolder": "customer"
      }
    }
  }'
```

Re-pointing `destination` to a healthy bank external account recovers a deposit address from `inactiveActionRequired` back to `active`. A re-point on an already `active` address updates the target without a status change. The new destination is validated exactly like create.

### No delete

There is no `DELETE` endpoint for deposit addresses. If you no longer want deposits on an address, stop sharing its inlet address.

## Related

* [Deposit addresses overview](/payments/deposit-addresses): concept summary and comparison with virtual accounts
* [Virtual accounts guide](/payments/guides/virtual-accounts): the fiat equivalent, a bank account number that auto-converts to crypto
* [Transaction lifecycle](/payments/core-concepts/transaction-lifecycle): statuses, sub-statuses, and webhook events for the auto-created transaction
