Wallet & Deposits
Read the prepaid balances and the wallet statement, fund the wallet with stablecoins through your own deposit addresses, and audit the deposit history.
Your workspace wallet is the prepaid balance every order spends from — one wallet per currency. This page covers reading balances, the wallet statement, funding with stablecoins, and auditing the deposit history. Reads require wallet.read; requesting a deposit address requires the dedicated wallet.deposit-address.write scope.
Read a balance
GET /api/v1/wallet/balance?currency=USD
{
"availableBalance": 425.00,
"reservedBalance": 75.00,
"currency": "USD",
"asOfUtc": "2026-09-05T12:00:00+00:00"
}availableBalance— spendable right now: new orders and withdrawals draw from this.reservedBalance— set aside: the totals of orders still being fulfilled (captured on fulfillment, released if they fail) and the amounts of withdrawals requested but not yet paid or cancelled.
currency is required: a three-letter currency code (any case). A malformed or unsupported code is 400 invalid_request; a currency in which the workspace holds no active wallet — none at all, or one that is frozen or closed — is 404 not_found.
Alert your operations well before available funds reach your hourly burn rate — an order admitted without sufficient funds is rejected, not queued.
Read every wallet
GET /api/v1/wallet/balances
Every wallet of the workspace, in currency order — a frozen or closed one too, saying which — with the reserved balance split by what it is held for:
{
"items": [
{
"currency": "USD",
"availableBalance": 425.00,
"reservedBalance": 75.00,
"ordersReserved": 52.50,
"withdrawalsHeld": 22.50,
"status": "active",
"asOfUtc": "2026-09-05T12:00:00+00:00"
}
]
}status is active, frozen (nothing can be ordered or withdrawn from it until Xegora releases it) or closed. ordersReserved and withdrawalsHeld add up to reservedBalance; when they did not add up exactly at the moment of the read (a change was being made), both are null rather than guessed — read again.
The wallet statement
GET /api/v1/wallet/movements?currency=USD&fromUtc=2026-09-01T00:00:00Z&toUtc=2026-10-01T00:00:00Z
Every movement of the workspace's wallets, newest first, each with the balance it left behind and what it was about — the same statement the dashboard shows — and, per currency, the window's opening and closing balance with its totals.
| Parameter | Meaning |
|---|---|
currency | A wallet currency to read (three letters, any case). Repeatable, at most ten; none reads every wallet. |
type | A movement code (below) to narrow the lines to. Repeatable; none shows every movement. |
fromUtc / toUtc | The window: fromUtc inclusive, toUtc exclusive. Leave toUtc out to keep the window open up to now; fromUtc defaults to 30 days before the window's end. The window must end after it starts and span at most 366 days. |
cursor | The next value of the previous page, to read the next (older) page. |
limit | 1–200 lines a page; default 50. A ceiling, not a promise — see Paging below. |
Any value the statement cannot read — an unknown code (including other), a malformed currency or more than ten of them, a cursor no page returned, a window that is reversed or too long, a limit out of range — is 400 invalid_request.
Paging: follow next, never count the lines
next, never count the linesWalk the statement until next is absent (null). A page shorter than limit is not the end of the statement.
limit is a ceiling, not a promise. Every line carries the balance it left behind, and a balance is only exact if every movement between two shown lines is counted — so when you narrow the lines with type, the statement walks the hidden movements in between as well, and it walks at most 10,000 of the workspace's movements for one page. When the lines you asked for lie further apart than that, the page ends early at the last line within reach and next carries on from exactly there. A page can therefore come back with a handful of lines, or even one, while next is still set.
The figures are never approximate: balanceAfter, reservedAfter and availableAfter are exact on every line a short page returns, and totalCount still counts every movement matching the filters in the whole window — compare your line count against totalCount rather than against limit. A page without a type filter shows nothing in between, so it fills to limit as long as movements remain.
{
"items": [
{
"id": "0198e001-7a1b-7c2d-8e3f-4a5b6c7d8e9f",
"occurredAtUtc": "2026-09-05T12:01:09+00:00",
"type": "order_charge",
"entry": "debit",
"balanceDelta": -52.50,
"reservedDelta": -52.50,
"currency": "USD",
"balanceAfter": 447.50,
"reservedAfter": 0.00,
"availableAfter": 447.50,
"order": {
"id": "0198e000-1111-7abc-9def-222233334444",
"publicReference": "ord_0198e00011117abc9def222233334444",
"clientReference": "your-order-1001"
},
"withdrawal": null,
"deposit": null,
"invoice": null
},
{
"id": "0198e000-1a2b-7c3d-8e4f-5a6b7c8d9e0f",
"occurredAtUtc": "2026-09-05T12:01:02+00:00",
"type": "order_hold",
"entry": "hold",
"balanceDelta": 0.00,
"reservedDelta": 52.50,
"currency": "USD",
"balanceAfter": 500.00,
"reservedAfter": 52.50,
"availableAfter": 447.50,
"order": {
"id": "0198e000-1111-7abc-9def-222233334444",
"publicReference": "ord_0198e00011117abc9def222233334444",
"clientReference": "your-order-1001"
},
"withdrawal": null,
"deposit": null,
"invoice": null
}
],
"next": "AAZHMdnm6MABmOAAERF6vJ3vIiIzM0RE",
"totalCount": 37,
"fromUtc": "2026-09-01T00:00:00+00:00",
"toUtc": "2026-10-01T00:00:00+00:00",
"summaries": [
{
"currency": "USD",
"openingBalance": 0.00,
"closingBalance": 447.50,
"credits": 1000.00,
"debits": 552.50,
"creditCount": 2,
"debitCount": 8,
"movementCount": 37
}
]
}Each line:
| Field | Meaning |
|---|---|
type | A stable movement code (below). |
entry | credit (the balance went up), debit (it went down), hold (part of it was set aside) or release (a hold was lifted). |
balanceDelta, reservedDelta | The signed change to the balance and to its reserved part. A hold or a release moves only the reserved part. |
balanceAfter, reservedAfter, availableAfter | The wallet after the movement: its balance, the reserved part, and what was available (balanceAfter − reservedAfter). The newest line's balanceAfter is the balance now. |
order, withdrawal, deposit, invoice | What the movement was about, when it was about one: the order (id, publicReference, clientReference), the withdrawal (id, publicReference), the deposit (id, network, currency, transactionHash), or the invoice the wallet paid (number, kind: setup or recurring). The others are null. |
The movement codes:
type | Entry | What moved the money |
|---|---|---|
deposit_credit | credit | A stablecoin deposit to one of your addresses was credited. |
payment_credit | credit | Xegora recorded a payment it received for your workspace. |
order_hold | hold | An order's total was reserved. |
order_charge | debit | A delivered order's reserved total was captured. |
order_release | release | An order's reservation was released: it was not delivered, or it was refunded before delivery. |
order_refund | credit | A delivered order was refunded to the wallet. |
withdrawal_hold | hold | A requested withdrawal's amount was set aside. |
withdrawal_payout | debit | A withdrawal was paid out. |
withdrawal_release | release | A withdrawal was cancelled or declined; its amount is available again. |
setup_fee | debit | The plan's setup invoice was paid from the wallet. |
recurring_fee | debit | A subscription invoice was paid from the wallet. |
adjustment_credit | credit | Xegora credited the wallet to put something right (a goodwill or correcting credit two of its operators approved). |
adjustment_debit | debit | Xegora debited the wallet to correct it. |
cashback_credit | credit | Your share of the cashback a fulfilled order earned. |
cashback_reversal | debit | That share taken back out of the order's refund, at the same instant as the order_refund credit — the refund undid the sale that earned it. |
A code never changes meaning, and a new kind of movement gets a new code; a movement without a published code reads other. Keep clients tolerant of codes you do not know.
The page's totalCount counts the movements matching the filters in the whole window; next is null on the last page — and only there, so keep following it even when a page came back short (above). Each summary covers one wallet over the window: openingBalance and closingBalance (at the window's end — now, for an open window), and the credits and debits (as positive amounts) with their counts, of the movements the filters match. Balances always count every movement, so with no type filter closingBalance = openingBalance + credits − debits; with one, the totals cover only the matching lines.
Fund with stablecoins
Your workspace gets its own permanent deposit address per network and stablecoin. Anything that arrives at an address, on its network, in its stablecoin, is credited to your wallet automatically once it is confirmed — no deposit "intents", no amounts to pre-declare.
See what can be deposited
GET /api/v1/wallet/deposit-options (scope: wallet.read)
[
{
"network": "<network code>",
"networkName": "<network name>",
"tokenStandard": "<token standard>",
"currency": "<stablecoin>",
"decimals": 6,
"confirmations": 20,
"minimumDeposit": 1.00,
"feePercent": 2.0,
"ledgerCurrency": "USD"
}
]The networks and stablecoins are whatever this endpoint returns — read them from it rather than hard-coding any. Only the options offered right now are listed; one may disappear while its network is under maintenance and come back later. Label from the response, key on the codes: networkName and tokenStandard are the names to show your team; network and currency are the stable identifiers to match on. Each option carries its own confirmations (how deep a transfer must be buried before it is recorded), minimumDeposit, and feePercent.
Request (or fetch) an address
PUT /api/v1/wallet/deposit-addresses (scope: wallet.deposit-address.write)
GET /api/v1/wallet/deposit-addresses (scope: wallet.read)
{ "network": "<network code>", "currency": "<stablecoin>" }Send a network and currency exactly as an option names them (each at most 16 characters). PUT is idempotent and immediate: it returns 200 OK with the address, and every later call for the same network and stablecoin returns the same one. GET lists every address the workspace has been issued.
{
"id": "0198db4d-f20c-765e-a89a-2b1b0b9a4f74",
"option": {
"network": "<network code>",
"networkName": "<network name>",
"tokenStandard": "<token standard>",
"currency": "<stablecoin>",
"decimals": 6,
"confirmations": 20,
"minimumDeposit": 1.00,
"feePercent": 2.0,
"ledgerCurrency": "USD"
},
"address": "<address on that network>",
"createdAtUtc": "2026-09-05T12:00:00+00:00",
"lastDepositAtUtc": null
}- Send only the stablecoin on the network of that address. Another token, or the same token on another network, sent to it cannot be credited.
addressis exactly what to send to, in the form its network uses (checksummed hex on EVM networks, base58 on TRON).- The address is permanent and reusable — print it into your treasury runbook once; deposits any time simply appear.
422with codedeposit_asset_not_offeredmeans the option is not offered right now;422 deposit_account_unavailablethat an address cannot be issued for the workspace right now (it holds no active wallet in the ledger currency, for example);503withdeposits_unavailable(and aRetry-After) that deposits are paused altogether.
The deposit fee
The deposit contract keeps the option's feePercent of every deposit; your wallet is credited the rest, in ledgerCurrency, one stablecoin unit to one currency unit. To know what to send so that an exact amount is credited:
GET /api/v1/wallet/deposit-quote?network=<network code>¤cy=<stablecoin>&amount=100 (scope: wallet.read)
{
"option": { "network": "<network code>", "currency": "<stablecoin>", "feePercent": 2.0, "...": "..." },
"credit": 100.00,
"sendAmount": 102.040816,
"feeAmount": 2.040816
}amount is the credit you want, in whole cents of the ledger currency: a value that is not positive is 400 invalid_request, one with more than two decimals 422 deposit_amount_invalid.
Credits are made in whole cents. What is left over below a cent after the fee is carried on the address and added to its next deposit, so nothing is lost; a deposit worth less than a cent on its own is shown as carried and is credited with the next one.
How crediting works
- Your transfer is recorded once it is the option's
confirmationsblocks deep (status: detected). - It is proved against the chain once more and credited to your wallet (
credited); adeposit_creditline appears on the statement and awallet.creditedwebhook fires withsource: "deposit"and the new available balance. - A deposit that cannot be credited automatically is
heldfor review by our operators; contact support with itsidandtransactionHash.
Audit the deposit history
GET /api/v1/wallet/deposits?limit=50
GET /api/v1/wallet/deposits?status=credited&detectedFrom=2026-09-01T00:00:00Z&detectedTo=2026-10-01T00:00:00Z
GET /api/v1/wallet/deposits/{depositId}
Keyset-paginated, newest first. Filters, all optional:
| Parameter | Meaning |
|---|---|
status | detected, credited, carried or held. |
network | A network code, as the deposit options name it. |
currency | A stablecoin code, as the deposit options name it. |
detectedFrom / detectedTo | A window on when the deposit was recorded: from inclusive, to exclusive; it must start before it ends. |
beforeDetectedAtUtc + beforeId | The cursor: the previous page's nextDetectedAtUtc and nextId, together (both or neither), passed back exactly as returned. |
limit | 1–100; default 50. |
An unknown status, network or stablecoin, a reversed window, half a cursor, or a value over 16 characters is 400 invalid_request.
{
"items": [
{
"id": "0198db4d-f2d9-7f8d-867d-329b081134a3",
"network": "<network code>",
"currency": "<stablecoin>",
"transactionHash": "<transaction hash>",
"explorerUrl": "<link to the transaction on a public block explorer>",
"grossAmount": 102.040816,
"feeAmount": 2.040816,
"netAmount": 100.000000,
"creditedAmount": 100.00,
"ledgerCurrency": "USD",
"status": "credited",
"detectedAtUtc": "2026-09-05T12:00:00+00:00",
"creditedAtUtc": "2026-09-05T12:00:20+00:00",
"networkName": "<network name>",
"address": "<your address the transfer was sent to>",
"blockNumber": 48123456,
"requiredConfirmations": 20
}
],
"nextDetectedAtUtc": null,
"nextId": null,
"totalCount": 1
}| Field | Meaning |
|---|---|
grossAmount, feeAmount, netAmount | What arrived, the fee the contract kept, and the rest, in the stablecoin. |
creditedAmount | What reached the wallet, in ledgerCurrency, in whole cents including any remainder carried from earlier deposits; 0 for a carried deposit, null until a deposit is credited. |
transactionHash | The transfer's hash in the form its network's explorers use: 0x-prefixed hex on EVM networks, plain hex on TRON. |
explorerUrl | A link to the transfer on a public block explorer, when one is known for the network. |
address | Your deposit address the transfer was sent to. |
blockNumber | The block the transfer is in. |
requiredConfirmations | The confirmations Xegora waits for on that network before it records a transfer — a listed deposit has at least this many; null when Xegora no longer supports the network. |
totalCount | How many deposits match the filters, across every page. |
GET /api/v1/wallet/deposits/{depositId} returns one deposit in the same shape; another workspace's deposit, or one that does not exist, is 404 not_found.
Treasury reconciliation loop
A robust daily loop:
- Read
GET /wallet/movementsfor the day, per currency; its summary'sopeningBalanceshould equal your books' closing balance of the day before, and every line names the order, withdrawal, deposit or invoice behind it. - Walk
GET /wallet/deposits(withdetectedFrom) back to your last stored deposit; match each row to your outgoing transfers bytransactionHash. - Compare
GET /wallet/balanceswith your ledger. - Raise any deposit still
detectedlong after its confirmation window, orheld, with support, quoting the depositidandtransactionHash.
Updated 13 days ago