# Customer deposit wallets

Customer wallets give a known customer a reusable deposit address. Relay associates confirmed deposits with your `userId`, calculates the configured fee, and queues the merchant amount for settlement to the saved organization destination.

Use this model for account funding and repeated deposits. Use a [payment intent](/docs/payments/crypto-payment-intents) when each order needs its own amount and lifecycle.

## Get or create a wallet

`POST /customer-wallets`

The operation is get-or-create for the organization, case-sensitive `userId`, network, and pinned chain identity. Calling it again returns the existing address, enrolls the requested token if needed, refreshes its saved settlement destination, and renews a two-hour fast-detection window.

### Request fields

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `userId` | string | yes | Your stable, organization-scoped customer ID. Case-sensitive; 1–128 characters. |
| `network` | string | yes | `tron` or `solana`. |
| `token` | string | yes | `USDT` or `USDC`, subject to active configuration. |

```bash
curl --request POST \
  --url https://bridge.relayfinance.io/customer-wallets \
  --header 'Content-Type: application/json' \
  --header 'accesskey: YOUR_ORGANIZATION_API_KEY' \
  --data '{
    "userId": "customer_28491",
    "network": "solana",
    "token": "USDC"
  }'
```

```json
{
  "success": true,
  "data": {
    "id": "cwallet_example",
    "assetId": "cwasset_example",
    "userId": "customer_28491",
    "network": "solana",
    "chainIdentity": "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
    "address": "HkYExampleOwnerAddress",
    "token": "USDC",
    "tokenContract": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "tokenAccount": "3pExampleAssociatedTokenAccount",
    "watchUntil": "2026-09-20T14:00:00.000Z",
    "retainedFees": "0.000000",
    "nextFeeSweepAt": "2026-10-01T00:00:00.000Z"
  },
  "meta": {
    "requestId": "7944aed7-cef2-4d8f-b942-3d8e4974f57f"
  }
}
```

### Response fields

| Field | Type | Description |
| --- | --- | --- |
| `id` | string | Wallet ID used by the read and history endpoints. |
| `assetId` | string | Enrollment ID for this wallet/token pair. |
| `userId` | string | Exact customer ID supplied by your system. |
| `network` | string | Wallet network. |
| `chainIdentity` | string | Pinned chain identity used for evidence validation. |
| `address` | string | Owner/deposit address. |
| `token` | string | Enrolled token. |
| `tokenContract` | string | Pinned token contract or mint. |
| `tokenAccount` | string or null | Solana associated token account; `null` on TRON. |
| `watchUntil` | string | End of the renewed fast-detection window. |
| `retainedFees` | decimal string | Confirmed, unreserved Relay fees waiting for the scheduled fee sweep. |
| `nextFeeSweepAt` | string | Next scheduled fee collection date. |

Wallet creation requires an active asset, token contract, configured customer-wallet fee policy, and saved settlement destination for the organization. Relay automatically sends the customer's net deposits to that saved token/network destination. If it is missing, wallet creation returns an error telling the organization to set it first. The saved destination cannot be the customer deposit address.

## Get a wallet

`GET /customer-wallets/{id}`

Returns the wallet and every token enrollment currently attached to it.

```json
{
  "success": true,
  "data": {
    "id": "cwallet_example",
    "userId": "customer_28491",
    "network": "solana",
    "address": "HkYExampleOwnerAddress",
    "assets": [
      {
        "assetId": "cwasset_example",
        "token": "USDC",
        "tokenAccount": "3pExampleAssociatedTokenAccount",
        "retainedFees": "0.000000"
      }
    ]
  },
  "meta": {
    "requestId": "946b12aa-7b1f-48b0-8653-ed71936617f6"
  }
}
```

## List deposits

`GET /customer-wallets/{id}/deposits?limit=20&offset=0`

| Query field | Type | Default | Limits |
| --- | --- | ---: | --- |
| `limit` | integer | `20` | 1–100 |
| `offset` | integer | `0` | 0–1,000,000 |

The endpoint returns an array in `data` and pagination details in `meta.pagination`.

```json
{
  "success": true,
  "data": [
    {
      "id": "deposit_example",
      "assetId": "cwasset_example",
      "transactionHash": "5TfExampleSignature",
      "blockTimestamp": "2026-09-20T11:51:04.000Z",
      "amount": "100.000000",
      "fee": "1.000000",
      "merchantAmount": "99.000000",
      "status": "confirmed",
      "createdAt": "2026-09-20T11:51:10.000Z",
      "updatedAt": "2026-09-20T11:51:10.000Z"
    }
  ],
  "meta": {
    "requestId": "57738670-36ad-46ed-8fb2-624ca1d39183",
    "pagination": {
      "total": 1,
      "limit": 20,
      "offset": 0,
      "hasMore": false
    }
  }
}
```

Deposit status starts as `confirmed` when it is eligible for settlement, or `review_required` when Relay cannot safely apply a fee and payout. After the linked merchant transfer is verified, the deposit becomes `settled`; an uncertain transfer can also move it to `review_required`.

## List transfers

`GET /customer-wallets/{id}/transfers?limit=20&offset=0`

Transfer records describe outgoing merchant payouts and periodic Relay fee collections.

| Field | Type | Description |
| --- | --- | --- |
| `kind` | string | `merchant` or `fee`. |
| `depositId` | string or null | Source deposit for a merchant transfer. |
| `destination` | string | Destination captured when the transfer was queued. |
| `amount` | decimal string | Exact outgoing amount. |
| `status` | string | `queued`, `submitted`, `settled`, or `review_required`. |
| `transactionHash` | string or null | On-chain hash after submission. |
| `lastError` | string or null | Safe operational status for deferred or reviewed work. |
| `nextAttemptAt` | string | Next scheduled processing or verification time. |

One outgoing operation runs at a time per wallet across all enrolled tokens. A `review_required` transfer blocks later outgoing work until Relay reconciles it; do not attempt to replace or resend the transfer yourself.

## Webhook events

Subscribe to the `wallet_event` action in the dashboard. Customer-wallet event types are:

- `customer_wallet.deposit_confirmed`
- `customer_wallet.deposit_review_required`
- `customer_wallet.merchant_settled`
- `customer_wallet.merchant_review_required`
- `customer_wallet.fee_settled`
- `customer_wallet.fee_review_required`
- `customer_wallet.transfer_review_required`

`deposit_confirmed` means the incoming funds were credited and a payout was queued. Only `merchant_settled` proves the outgoing merchant payout was verified on-chain.

## Common errors

| HTTP | Message | Cause |
| ---: | --- | --- |
| `400` | `Asset is unavailable` | Inactive asset, missing contract, or unsupported configuration |
| `400` | `Customer wallet fees are not configured for this asset` | No applicable fee policy |
| `400` | saved settlement error | No approved settlement destination for the network/token |
| `400` | `Wallet asset contract changed; reconciliation required` | Existing wallet enrollment is pinned to another contract |
| `404` | `Customer wallet not found` | The ID is absent or belongs to another organization |
