# Balances & deposits

Read spendable balances and incoming deposit history

2 endpoints. Generated from `docs/openapi.yaml` v1.0.0.

| Endpoint | Summary |
| --- | --- |
| [`GET /v1/users/{externalId}/balances`](#get-v1-users-externalid-balances) | Get a user's spendable balances |
| [`GET /v1/users/{externalId}/deposits`](#get-v1-users-externalid-deposits) | List a user's deposits |

## GET /v1/users/{externalId}/balances

**Get a user's spendable balances**  ·  _Auth: bearer API key_  ·  `operationId: getUserBalances`

**Spendable, always net of holds.** A pending withdrawal places
its hold at REQUEST time — the held amount (plus fees) leaves the user's
balance immediately and sits in a pending account until the operation
confirms or is refunded.

The guarantee runs in one direction and is safe to rely on: while an
operation is in flight this endpoint reads **lower** than the user's
eventual balance, **never higher**. It cannot report a surplus that a
queued payout is about to consume. If the operation fails, the hold is
reversed and the balance returns.

Assets with no activity are absent from the response. So is an empty
account on a chain the platform no longer serves (`Chain`) — a user from
before a chain was retired keeps such rows, and they are listed only
while they hold a balance.

`on_chain` is what the user's own address holds at this moment, for
comparison with a block explorer. It is not the spendable figure and
the two differ by design (see the `Balance` schema).

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `externalId` | path | `string` | yes | Your `external_user_id` for the user. |

#### Responses

| Status | Body | Meaning |
| --- | --- | --- |
| `200` | `object[]` | Per-asset balances. |
| `401` | `Error` | Missing, malformed or unknown API key. |
| `404` | `Error` | Unknown external_user_id for this tenant. |

**`200` response body**

| Field | Type | Description |
| --- | --- | --- |
| `[].asset_id` **required** | `string` | — |
| `[].symbol` **required** | `string` | — |
| `[].chain` **required** | `string` | The chain an address or asset is on. TRON is the only network the platform serves: `btc`, `eth`, `bsc` and `sol` were retired — historical addresses on them are never handed out again and nothing scans them. Read the value as an open string rather than a closed list, so a chain added later does not break your client. _(one of `tron`)_ |
| `[].amount` **required** | `string` | A monetary value as a decimal string. Always paired with a `*_base` field that carries the exact integer base-unit value; compute with the `_base` string. |
| `[].amount_base` **required** | `string` | Exact integer amount in the asset's smallest unit. _(pattern `^[0-9]+$`)_ |
| `[].on_chain` **required** | `string` | What the user's own deposit address holds right now, read live from the chain. Informational: `amount` is the balance the platform enforces. The two differ by design — fees leave the address for the platform's profit wallet as they settle, earnings (commission, a merchant's own fees) are credited from there, and a balance from before 4 September 2026 lives in the platform's wallets. `null` when the address could not be read in time. |
| `[].on_chain_base` **required** | `string` | Exact integer amount in the asset's smallest unit. _(pattern `^[0-9]+$`)_ |

```json
[
  {
    "asset_id": "usdt_trc20",
    "symbol": "USDT",
    "chain": "tron",
    "amount": "1.5",
    "amount_base": "1500000000",
    "on_chain": "1.5",
    "on_chain_base": "1500000000"
  }
]
```

#### Example request

```bash
curl -X GET 'https://api.cryptoinfra.build/v1/users/string/balances' \
  -H 'Authorization: Bearer ck_live_your_key'
```

## GET /v1/users/{externalId}/deposits

**List a user's deposits**  ·  _Auth: bearer API key_  ·  `operationId: getUserDeposits`

Most recent deposits first.

The optional range filters on when the deposit was FIRST SEEN on-chain, not on
when it was credited — so a still-pending deposit cannot fall out of a window
that covers it. `from`/`to` accept either a bare IST calendar day
(`YYYY-MM-DD`, both ends included) or a full RFC 3339 instant with an offset.

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `externalId` | path | `string` | yes | Your `external_user_id` for the user. |
| `limit` | query | `integer` | no | — _(default `50`; min `1`; max `200`)_ |
| `offset` | query | `integer` | no | — _(default `0`; min `0`)_ |
| `from` | query | `string` | no | Only deposits first seen on/after this IST day (or instant). |
| `to` | query | `string` | no | Only deposits first seen on/before this IST day (or before this instant). |

#### Responses

| Status | Body | Meaning |
| --- | --- | --- |
| `200` | `object[]` | Deposit history. |
| `400` | `Error` | Body/params failed validation, or a domain rule was violated. `error` is one of: validation_error, unknown_asset, asset_disabled, invalid_amount, amount_below_minimum, bad_amount, invalid_address, invalid_request. |
| `401` | `Error` | Missing, malformed or unknown API key. |
| `404` | `Error` | Unknown external_user_id for this tenant. |

**`200` response body**

| Field | Type | Description |
| --- | --- | --- |
| `[].id` **required** | `integer` | — |
| `[].chain` **required** | `string` | The chain an address or asset is on. TRON is the only network the platform serves: `btc`, `eth`, `bsc` and `sol` were retired — historical addresses on them are never handed out again and nothing scans them. Read the value as an open string rather than a closed list, so a chain added later does not break your client. _(one of `tron`)_ |
| `[].asset_id` **required** | `string` | — |
| `[].symbol` **required** | `string` | — |
| `[].address` **required** | `string` | — |
| `[].from_address` | `string` | Who paid it. For a token, the `from` of the Transfer event to `address`, not the transaction's signer; for TRX, the sender. `null` only for an old deposit whose sender could not be read. |
| `[].txid` **required** | `string` | — |
| `[].output_index` **required** | `integer` | Position of the transfer inside its transaction (the log index of a TRC20 transfer; 0 for a native TRX transfer). |
| `[].amount` **required** | `string` | Gross on-chain amount. |
| `[].amount_base` **required** | `string` | Exact integer amount in the asset's smallest unit. _(pattern `^[0-9]+$`)_ |
| `[].fee` **required** | `string` | Platform deposit fee. Deposits are free, so this is `0` on every deposit since 2026-09-04. |
| `[].fee_base` **required** | `string` | Exact integer amount in the asset's smallest unit. _(pattern `^[0-9]+$`)_ |
| `[].amount_credited` **required** | `string` | Net credited to the user (amount - fee). |
| `[].amount_credited_base` **required** | `string` | Exact integer amount in the asset's smallest unit. _(pattern `^[0-9]+$`)_ |
| `[].status` **required** | `string` |  _(one of `seen`, `confirming`, `credited`, `orphaned`, `ignored_dust`)_ |
| `[].block_height` | `integer | null` | — |
| `[].confirmations` **required** | `integer` | — |
| `[].first_seen_at` **required** | `string<date-time>` | — |
| `[].credited_at` | `string<date-time> | null` | — |

```json
[
  {
    "id": 1,
    "chain": "tron",
    "asset_id": "string",
    "symbol": "string",
    "address": "string",
    "from_address": "string",
    "txid": "string",
    "output_index": 1,
    "amount": "1.5",
    "amount_base": "1500000000",
    "fee": "1.5",
    "fee_base": "1500000000",
    "amount_credited": "1.5",
    "amount_credited_base": "1500000000",
    "status": "seen",
    "block_height": 1,
    "confirmations": 1,
    "first_seen_at": "2026-08-31T09:15:00.000Z",
    "credited_at": "2026-08-31T09:15:00.000Z"
  }
]
```

#### Example request

```bash
curl -X GET 'https://api.cryptoinfra.build/v1/users/string/deposits?from=2026-08-01&to=2026-08-31' \
  -H 'Authorization: Bearer ck_live_your_key'
```
