Practice wallet
Every account has a play-money wallet in ATLAS, with free top-ups, running the same order path as real money: real venue prices, real settlement, one code path. Ship the whole product before anyone deposits a dollar.
What you skip
OpenMarkets handles
- A ledger per account, and the balance on it
- Fills at the best live price on a real venue, on the internal OpenMarkets book
- Settlement and grading through the same resolution path as a real order
- Per-model and per-currency performance that never blends ATLAS with USD
You do
- Send
"mode": "paper"on a leg - Show the ATLAS balance next to, never added to, real money
- Top up when a user runs dry
Paper is a mode, not an environment
Any execution credential can place a paper order — a live key, a test key, an app's install key, a person's session — by putting "mode": "paper" on the leg. The order books against the OpenMarkets practice venue at the venue quote you named, and settles when the market does.
{
"model_id": "m_…", // optional — file it under a model
"orders": [
{ "liquidity_hash": "kalshi:ct_abc123:…", "amount": 25, "max_price": 0.62, "mode": "paper" }
]
}
- On a live key,
modedefaults toreal; paper must be asked for. - On a test key, a leg without
modeis rejected rather than assumed — see Test mode. - A position is paper-bettable when it has a registered resolver and a real quote behind it; the liquidity feed says so per position with
paper_bettable. - Placing needs
trade:execute(Pro and up, or an app that holds it for the user); reading the wallet needsaccount:read.
/flow/v1/auth/account/paperThe practice wallet: the ATLAS balance, the largest single top-up, and the cap the balance may not exceed. Acts on the key's own account, or on a Connect user with X-OpenMarkets-Account.
/flow/v1/auth/account/paper{
"currency": "ATLAS",
"balance": 1000,
"max_deposit": 100000,
"balance_cap": 10000000
}
/flow/v1/auth/account/paper/depositTop the wallet up. Body { amount, description? } — amount above 0, at most two decimals, at most max_deposit (400 invalid_amount), and the result may not exceed balance_cap (409 balance_cap). Needs trade:execute, the same gate as placing a paper order. Send an Idempotency-Key; the reply is 201 with the new balance and the ledger entry.
// Request body
{ "amount": 500, "description": "weekly top-up" }
// 201 · Response.data
{ "currency": "ATLAS", "amount": 500, "balance": 1500, "om_ledger_id": "led_…" }
/flow/v1/auth/account/paper/ledgerThe wallet's ledger, newest first. type is one of opening_balance, bet_debit, settlement_credit, refund, adjustment, deposit. Each entry names the order that moved it, when one did.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| limit | query | integer | no | Max 200 |
/flow/v1/auth/account/paper/ledger?limit=50Max 200
{
"currency": "ATLAS",
"entries": [
{ "ledger_id": "led_…", "type": "settlement_credit", "amount": 40.32, "balance_after": 1515.32,
"router_order_id": "ro_…", "description": null, "created_at": "2026-09-22T04:10:00Z" },
{ "ledger_id": "led_…", "type": "bet_debit", "amount": -25, "balance_after": 1475,
"router_order_id": "ro_…", "description": null, "created_at": "2026-09-22T03:02:11Z" },
{ "ledger_id": "led_…", "type": "deposit", "amount": 500, "balance_after": 1500,
"router_order_id": null, "description": "weekly top-up", "created_at": "2026-09-21T18:00:00Z" }
]
}
Where else it shows up
GET /auth/account/balancesreturns anATLASbucket beside theUSDone — see Venues & balances.GET /auth/performanceand every model read partition by currency, so a paper strategy and a real one never share a number — see Models & attribution.- A Connect flow can be marked practice-only, so an organization's users trade in ATLAS without linking a real venue at all.
Coming next
OM-08 · Wallet SDK · SoonReal-money balances, paid features and payouts, on the same wallet model. Planned, not available; nothing here promises a date. The practice wallet is the live rung.