> ## Documentation Index
> Fetch the complete documentation index at: https://tetrafi.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Balances and fees

> Read every balance your workspace holds across wallets and exchange accounts, one exchange account's balances and deposits, and the fees you pay.

Read what each wallet and exchange account holds and the TetraFi fees you pay; these reads never move money.

## At a glance

Paths are under `/api/v1/workspaces/{workspaceId}`.

| Endpoint | Permission | Result |
| - | - | - |
| `GET …/balances` | Treasury read | Every wallet and exchange-account balance, one row per account, asset and chain. |
| `GET …/venue-enablements` | Treasury read | Your exchange accounts and their `venueAccountId`. |
| `GET …/venue-accounts/{venueAccountId}/balances` | Treasury read | What the venue last reported for one account. |
| `GET …/venue-accounts/{venueAccountId}/deposit-instructions` and `…/deposit-history` | Treasury read | Where to deposit, and what was credited. |
| `GET …/venue-accounts/{venueAccountId}/credit` and `…/ledger` | Treasury read | Credit and positions: [Trades and settlement](https://tetrafi.mintlify.site/trade/settlement-and-credit.md#credit-and-the-ledger). |
| `GET …/fee-schedule` | Read fee schedule | The fees in force now and every scheduled change. |

Your workspace id is the `workspaceId` from `GET /api/v1/me` ([Permissions](https://tetrafi.mintlify.site/authentication.md#permissions)).

## Every balance in one list

`GET …/balances` reads TetraFi's last observation and never asks a chain or venue. Filter with `asset`, `accountKind` and `accountId`; an account that cannot be stated is listed in `unavailable`, never as a zero row.

> [accordion] Fields and paging
>
> Personal wallets are never listed; bank account balances cannot be observed.
>
> | Field | Meaning |
> | - | - |
> | `id` | Opaque: never parse or build one. |
> | `accountKind` | `wallet`, `venueAccount` or `bankAccount`; show an unknown kind as it is. |
> | `accountId` | A wallet's `walletId`, or the exchange account's `venueAccountId`. |
> | `asset`, `chain` | The asset and CAIP-2 chain; `chain` is `null` off-chain, such as at an exchange. |
> | `nativeAssetId` | The asset's id at its source, such as a token contract, if known. |
> | `amount` | Everything held, a decimal string in whole units. |
> | `asOf`, `freshness` | When observed, and `fresh`, `stale` or `partial` (treat an unknown value as `stale`). |
>
> Page with `limit` (1 to 100) and `cursor` until `nextCursor` is `null`; if accounts change mid-paging, restart without a cursor.

> [accordion] Request and response
>
> ```bash cURL theme={null}
> # GET …/balances lists one row per account, asset and chain. Pass nextCursor back as cursor until
> # it is null. amount is a decimal string in whole asset units; asOf is when it was observed.
> rows='[]' cursor='' pages=0
> for _ in 1 2 3 4 5; do
>   page=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/balances?limit=50${cursor:+&cursor=$cursor}")
>   pages=$((pages + 1))
>   rows=$(jq -c --argjson rows "$rows" '$rows + .items' <<<"$page")
>   cursor=$(jq -r '.nextCursor // empty' <<<"$page")
>   [[ -z $cursor ]] && break
> done
> jq -c '.[:3][] | {accountKind, accountId, asset, chain, amount, asOf, freshness}' <<<"$rows" >&2
> ```
>
> ```json Response (shortened) theme={null}
> {
>   "items": [
>     { "id": "…", "accountKind": "wallet", "accountId": "…", "asset": "USDC", "chain": "eip155:84532",
>       "nativeAssetId": "0x036c…", "amount": "12.200749", "asOf": "2026-11-05T21:40:11Z", "freshness": "fresh" }
>   ],
>   "nextCursor": null,
>   "unavailable": []
> }
> ```
>
> ```bash cURL theme={null}
> # Narrow the list with asset, accountKind (wallet, venueAccount or bankAccount) and accountId.
> kind=${TETRAFI_BALANCE_KIND:-wallet}
> filtered=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/balances?accountKind=$kind&limit=50")
> jq -c '.items[:3][] | {accountKind, accountId, asset, amount}' <<<"$filtered" >&2
> ```

> [accordion] Balances that are unknown
>
> An account whose balance cannot be stated is listed in `unavailable` on every page: show it as unavailable, not empty. Each entry has `accountKind`, `accountId`, `asset` and `chain` when known, a `reasonCode`, and the `asOf` of the source's last answer.
>
> `incomplete` means the list may leave accounts out; `reason` `accessSettingsUnavailable` says access settings could not be read. Read again shortly.
>
> ```bash cURL theme={null}
> # An account whose balance cannot be read is listed in unavailable with its reasonCode, on every
> # page. It is never shown as a zero row: show it as "unavailable". incomplete, when present, means
> # the workspace's access settings could not be read, so the list may leave accounts out.
> jq -c '.unavailable[:3][] | {accountKind, accountId, asset, reasonCode}' <<<"$page" >&2
> incomplete=$(jq -r '.incomplete.reason // empty' <<<"$page")
> [[ -n $incomplete ]] && echo "{\"incomplete\":\"$incomplete\"}" >&2
> ```
>
> ```json Response (shortened) theme={null}
> {
>   "items": [],
>   "nextCursor": null,
>   "unavailable": [
>     { "accountKind": "venueAccount", "accountId": "169e142e-…", "asset": null, "chain": null, "reasonCode": "…", "asOf": null }
>   ],
>   "incomplete": { "reason": "accessSettingsUnavailable" }
> }
> ```

## List your exchange accounts

`GET …/venue-enablements` lists venue accounts in `items`, oldest first, keyed by `venueEnablementId`. Read balances from a `configured` account; a `walletBased` venue trades from your wallets, so read those.

> [accordion] Fields and paging
>
> Page with `limit` (20 by default, at most 100) and `cursor`.
>
> | Field | Meaning |
> | - | - |
> | `accountId` | The `venueAccountId` the reads below take. Absent for a wallet-based venue. |
> | `venueKind` | `accountBased` (the venue holds your money) or `walletBased` (it trades from your wallets: read those). |
> | `fundingTarget` | Where the money lives: `{kind: "venueAccount", accountId}` or `{kind: "tradingWallets", walletIds}`. Treat another kind as "other". |
> | `venueId`, `label` | The venue, such as `binance` (`null` if unknown), and the account's app name. |
> | `lifecycle` | `pending`, `configured`, `funding_only`, `disabled` or `revoked`. Read balances from `configured`; treat others as unusable. |
> | `tradePaused`, `withdrawalPaused` | Whether trading or withdrawals are paused. |

> [accordion] Request and output
>
> ```bash cURL theme={null}
> # GET …/venue-enablements lists the workspace's exchange accounts, oldest first: pass nextCursor
> # back as cursor until it is null. Each item's accountId is the {venueAccountId} of the reads below.
> # Take one that is configured and holds its money at the venue (venueKind accountBased).
> account='' cursor=''
> for _ in 1 2 3 4 5; do
>   page=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/venue-enablements?limit=20${cursor:+&cursor=$cursor}")
>   jq -c '.items[:3][] | {accountId, venueId, label, lifecycle, venueKind}' <<<"$page" >&2
>   account=$(jq -r 'first(.items[] | select(.lifecycle == "configured" and .venueKind == "accountBased")
>     | .accountId) // empty' <<<"$page")
>   cursor=$(jq -r '.nextCursor // empty' <<<"$page")
>   [[ -n $account || -z $cursor ]] && break
> done
> ```
>
> ```json Output on test networks theme={null}
> {"accountId":"8a31ab17-…","venueId":"okx","label":"OKX account","lifecycle":"revoked","venueKind":"accountBased"}
> {"accountId":"7e873d0e-…","venueId":"okx","label":"OKX account","lifecycle":"revoked","venueKind":"accountBased"}
> {"accountId":"169e142e-…","venueId":"keyrock","label":"Keyrock account","lifecycle":"configured","venueKind":"accountBased"}
> ```

## One exchange account

`GET …/venue-accounts/{venueAccountId}/balances` answers what the venue last reported for the account, one line per asset. `available` is `free` less TetraFi's commitments to open trades and withdrawals.

> [accordion] Fields
>
> | Field | Meaning |
> | - | - |
> | `state` | `supported` when readable; otherwise `reason` and `reasonCode` say why. |
> | `observedAt`, `expiresAt` | When reported, and when the reading stops being current. |
> | `balances` | One line per asset. `null` means no reading; `[]` means the account holds nothing. |
> | `balances[].total` | Everything held, locked included. Negative on a credit account: what it owes. |
> | `balances[].free` | Free to trade or withdraw, per the venue. |
> | `balances[].available` | `free` less TetraFi's commitments to open trades and withdrawals; `null` if unknown. |
> | `balances[].usdValue` | US dollar value; `null` without a reference price, never `0`. |

> [accordion] Request and output
>
> ```bash cURL theme={null}
> # GET …/venue-accounts/{venueAccountId}/balances reads the account's balances as the venue last
> # reported them. balances null means no observation; [] is a real empty account. available is the
> # venue's free balance less TetraFi's open trades and withdrawals, null when unknown; usdValue is
> # null without a reference price, never 0. A balance never authorizes a payment.
> balances=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/venue-accounts/$account/balances")
> jq -c '{state, observedAt, expiresAt, balances: (.balances | if . == null then null else
>   [.[] | {asset, total, free, available, usdValue}] end)}' <<<"$balances" >&2
> ```
>
> ```json Output on test networks expandable theme={null}
> {"state":"supported","observedAt":"2026-11-02T13:20:05.236Z","expiresAt":"2026-11-02T13:25:05.236Z","balances":[{"asset":"SOL","total":"0.31240084","free":"0.31240084","available":"0.31240084","usdValue":"29.2500906492"},{"asset":"USDC","total":"7393.12791400","free":"7337.12791400","available":"7337.127914","usdValue":"7391.6492884172"},{"asset":"XRP","total":"15.153715","free":"15.153715","available":"15.153715","usdValue":null}]}
> ```

> [accordion] Deposits
>
> | Read | Answers |
> | - | - |
> | `GET …/venue-accounts/{venueAccountId}/deposit-instructions` | The asset and network pairs it takes deposits on, in `options`. |
> | `GET …/venue-accounts/{venueAccountId}/deposit-history` | Deposits the venue credited for one `asset` on one `chainRef` (CAIP-2), both required, over the last `lookbackDays` (1 to 30, 7 by default). Returned whole, with `since` and `until` and no cursor. |
>
> In the history, `credits: null` means it could not be read; `[]` is truly empty. Over 1,000 venue records answers no credits with `reasonCode` `DEPOSIT_HISTORY_WINDOW_TOO_LARGE`: read a shorter window.
>
> ```bash cURL theme={null}
> # GET …/deposit-instructions lists the asset and network pairs the account takes deposits on
> # (options); GET …/deposit-history?asset=…&chainRef=… lists the deposits the venue credited on one
> # of them. credits null means the history is unavailable (reason says why), never an empty history.
> instructions=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/venue-accounts/$account/deposit-instructions")
> read -r asset chain < <(jq -r '(.options // [])[0] | "\(.asset // "") \(.chainRef // "")"' <<<"$instructions")
> if [[ -z $asset ]]; then
>   # A venue that declares no deposit network answers options [] and names its assets in
>   # unconfirmedNetworkAssets: ask for one of them on a chain GET /api/v1/assets lists for it.
>   asset=$(jq -r '.unconfirmedNetworkAssets // [] | if index("USDC") then "USDC" else .[0] // "" end' <<<"$instructions")
>   chain=$(tetrafi GET /api/v1/assets | jq -r --arg a "$asset" 'first(.items[] | select(.assetId == $a)
>     | .variants[] | select(.kind == "native") | .deployments[] | .chain) // ""')
> fi
> [[ -n $asset && -n $chain ]] || fail NO_DEPOSIT_OPTION
> history=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/venue-accounts/$account/deposit-history?asset=$asset&chainRef=$chain")
> jq -c '{asset, chainRef, state, reason, credits: (.credits | if . == null then null else length end)}' <<<"$history" >&2
> ```
>
> ```json Output on test networks theme={null}
> {"asset":"USDC","chainRef":"eip155:11155111","state":"supported","reason":"Terminal credits reported by this venue account in the displayed window. Refresh balances separately before requesting a fresh RFQ.","credits":0}
> ```

> [!WARNING]
>
> A venue that publishes no networks answers `options: []` and lists its assets in `unconfirmedNetworkAssets`. Their networks are not verified: never send real funds on them.

## Fee schedule

`GET …/fee-schedule` answers the schedule in force now (`current`) and every change already scheduled (`upcoming`); it reads only. Every member can read fees, but a key needs Read fee schedule: no other permission includes it.

> [accordion] Request and response
>
> ```bash cURL theme={null}
> # GET …/fee-schedule answers the schedule in force (current) and the changes already scheduled
> # (upcoming), oldest first. Every rate is a decimal string in parts per million: 100 ppm is one
> # basis point. A venue listed in venues uses its own rates; every other venue uses defaultRates.
> schedule=$(tetrafi GET "/api/v1/workspaces/$TETRAFI_WORKSPACE_ID/fee-schedule")
> jq -c '.current.defaultRates | {platform: .platform.ratePpm, grossPlatform: .grossPlatform.ratePpm,
>   takerIncentive: (.takerIncentive.ratePpm // null)}' <<<"$schedule" >&2
> jq -c '.current.venues[:3][] | {venueId, platform: .rates.platform.ratePpm}' <<<"$schedule" >&2
> jq -c '.upcoming[] | {effectiveFrom, platform: .schedule.defaultRates.platform.ratePpm}' <<<"$schedule" >&2
> ```
>
> ```json Response (shortened) theme={null}
> {
>   "workspaceId": "36ff6481-…",
>   "current": {
>     "defaultRates": {
>       "platform": { "ratePpm": "100", "effectiveFrom": "2026-10-27T00:00:00Z" },
>       "grossPlatform": { "ratePpm": "100", "effectiveFrom": "2026-10-27T00:00:00Z" },
>       "takerIncentive": null,
>       "makerShare": null
>     },
>     "venues": [],
>     "partnerArrangementApplies": false
>   },
>   "upcoming": []
> }
> ```

> [accordion] Schedule fields and rates
>
> **current** `object`
>
> The schedule in force.
>
> **schedule**
>
> **defaultRates** `object`
>
> The rates every venue uses unless it is listed in `venues`.
>
> **venues** `array`
>
> The venues whose rates differ, each with its own `rates`.
>
> **partnerArrangementApplies** `boolean`
>
> `true` when your workspace has a partner arrangement with the venue.
>
> **upcoming** `array`
>
> Scheduled changes, oldest first, each with its `effectiveFrom` and the whole `schedule` from then. `[]` means none.
>
> | Rate | Meaning |
> | - | - |
> | `platform` | Your charged rate, after any taker incentive. |
> | `grossPlatform` | The rate before the taker incentive. |
> | `takerIncentive` | The share of the gross fee rebated to you; `null` if none. |
> | `makerShare` | The fee share you earn as the venue's partner maker; `null` if none. Never shows another workspace's arrangement. |
>
> Each rate has `ratePpm` and `effectiveFrom`:
>
> * `ratePpm`: a decimal string in parts per million (100 ppm is one basis point, 0.01%). Read it as a decimal, not a float.
> * `effectiveFrom`: the RFC 3339 date-time it applies from.

## Refusals

> [accordion] Balances and exchange accounts
>
> | `error` | Status | What to do |
> | - | - | - |
> | `INVALID_BALANCE_LIST_QUERY` | 400 | Use only `cursor`, `limit`, `asset`, `accountKind` (`wallet`, `venueAccount` or `bankAccount`) and `accountId`. |
> | `INVALID_LIMIT`, `INVALID_CURSOR` | 400 | Send a valid `limit`, or start again without a cursor. |
> | `OBSERVATION_AUTHORITY_REQUIRED`, `VENUE_READ_AUTHORITY_REFUSED` | 403 | The key has no current authority to read the account, or lost it mid-read. Restore its Treasury read access. |
> | `WALLET_ACCESS_CHANGED` | 403 | The key's access changed mid-read. Read again. |
> | `WORKSPACE_ACCESS_REQUIRED` | 403 | The key's owner left the workspace. Use a current member's key. |
> | `VENUE_ACCOUNT_NOT_FOUND` | 404 | No exchange account with that id in the workspace. |
> | `VENUE_ACCOUNT_ELIGIBILITY_CHANGED` | 409 | An account was disconnected mid-read. Read again. |
> | `VENUE_ACCOUNT_REVERIFY_REQUIRED`, `VENUE_ACCOUNT_REVOKED` | 409 | No current venue key reads the account, or it was disconnected. Reconnect it in the app. |
> | `VENUE_ACCOUNT_FUNDS_IN_WALLETS` | 409 | The money is in your own wallets: read those. |
> | `INVENTORY_UNAVAILABLE`, `VENUE_ACCOUNT_ELIGIBILITY_UNAVAILABLE` | 503 | Retry shortly. |

> [accordion] Fee schedule
>
> | `error` | Status | What to do |
> | - | - | - |
> | `INVALID_REQUEST` | 400 | The path's workspace id is not a UUID. |
> | `FEE_SCHEDULE_UNAVAILABLE` | 503 | Unreadable; no partial or zero schedule is sent. Retry shortly. |
>
> A key without Read fee schedule is refused with `403`.

Every other code is in [Error codes](https://tetrafi.mintlify.site/reference/error-codes.md).

> [card] [Run the balances script](https://github.com/TetraFi/docs/blob/main/samples/http/venue-balances.sh)
>
> Lists exchange accounts, then reads one's balances and deposits.

> [card] [Run the fees script](https://github.com/TetraFi/docs/blob/main/samples/http/fee-schedule.sh)
>
> Reads your current and upcoming fees.

## Next

> [card] [Trades and settlement](https://tetrafi.mintlify.site/trade/settlement-and-credit.md)
>
> Every trade, its settlement, and credit.

> [card] [Errors and refusals](https://tetrafi.mintlify.site/trade/errors.md)
>
> Fix, retry or recover.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.