Skip to main content

Model

interface Balance {
  token: string;
  available: string;
  unsettled: string;
  locked: string;
  quarantined: string;
}
Total holdings = available + locked. unsettled and quarantined are reserved for future settlement modes and currently always zero.

Reading balances

GET /api/me/balances
X-Api-Key: ps_live_<keyId>_<secret>  # integrators — needs `portfolio:read`
Response:
[{ "token": "USDC", "available": "1248.22", "unsettled": "0", "locked": "75.00", "quarantined": "0" }]

How locked moves

Eventavailablelocked
Order placed (BUY)-notional+notional
Order placed (SELL)00
Order cancelled+residual-residual
Trade executed (BUY)0-notional
Trade executed (SELL)+proceeds − fee0
Order rejected+full-full
Market resolved+residual-residual
Withdrawal requested-amount+amount
Withdrawal confirmed on-chain0-amount
Withdrawal rejected / reverted+amount-amount
SELL orders don’t move USDC balances — they encumber the outcome shares in vault_erc1155_locks instead. After you place a SELL, your available and locked USDC are unchanged; what does change is the available-to-promise outcome-share balance for the underlying token. Inspect this via GET /api/me/positions (the quantity reflects post-encumbrance available shares). When the SELL fills on-chain, USDC proceeds land in available per the table above.

Append-only ledger

Every balance change is mirrored to balance_events (append-only).
GET /api/me/balance-events?from=2026-04-01&limit=500
X-Api-Key: ps_live_<keyId>_<secret>  # integrators — needs `portfolio:read`
Invariant. For any (user, token): SUM(deltaAvailable) + SUM(deltaLocked) === current(available) + current(locked).

Getting testnet balance

POST /api/faucet
X-Api-Key: ps_live_<keyId>_<secret>  # integrators — needs `portfolio:read`
Content-Type: application/json

{ "amount": "10000" }
Faucet is testnet-only. See Environments.