Sandbox API
Endpoints for configuring and managing the sandbox (paper-trading) state of a client account. All endpoints require a sandbox API key (seq_test_*); calling them with a seq_live_* key returns 403 Forbidden.
Sandbox positions live in the same positions_v2 table as live positions, with is_sandbox = true. As a result, GET /v1/positions returns sandbox rows when called with a sandbox key — there is no separate read endpoint.
POST /v1/sandbox/positions
Configure explicit paper positions per (venue, kind, symbol). Writes to positions_v2 (so GET /v1/positions reflects the configured state) and mirrors cash rows into the SOR's in-memory balance cache (so reservations succeed on the order hot path).
Body
{
"positions": [
{ "venue": "polymarket", "kind": "crypto", "symbol": "USDC", "qty": 10000.0 },
{ "venue": "kalshi", "kind": "fiat", "symbol": "USD", "qty": 5000.0 },
{ "venue": "kalshi", "kind": "event_contract",
"symbol": "KXMARKET-FOO:YES", "qty": 100, "entry_price_usd": 0.40 },
{ "venue": "hyperliquid","kind": "perp",
"symbol": "BTC-USD-PERP", "qty": 0.5, "entry_price_usd": 92000.0 }
],
"replace": true
}| Field | Type | Notes |
|---|---|---|
positions[].venue | string | e.g. polymarket, kalshi, kraken, coinbase, dex, hyperliquid. |
positions[].kind | string | One of fiat, crypto, event_contract, perp. |
positions[].symbol | string | For fiat/crypto: asset code (USD, USDC, BTC). For event_contract: Kalshi ticker (KXFOO-...:YES/:NO) or Polymarket conditional token id. For perp: full symbol (BTC-USD-PERP). |
positions[].qty | number | Human-readable quantity. Must be ≥ 0 for fiat/crypto/event_contract. Perps may be negative (short). |
positions[].entry_price_usd | number? | Optional cost basis per unit. Defaults: 1.0 for USD-pegged stablecoins/fiat, 0.0 otherwise. |
replace | bool | If true, deletes all existing sandbox positions for the client first. Default false (upsert by (venue, kind, symbol)). |
Response
{ "written": 4, "deleted": 7 }Symbol-shape parsing for event_contract
| Input | Parsed (market_slug, outcome, token_id) |
|---|---|
KXFOO-BAR:YES | ("KXFOO-BAR", "YES", None) |
KXFOO-BAR:NO | ("KXFOO-BAR", "NO", None) |
1155562638... (≥30 digits) | (symbol, "YES", Some(symbol)) — Polymarket token id |
MARKET-SLUG | (symbol, "YES", None) |
To configure a Polymarket NO position, pass the NO outcome's token id directly.
POST /v1/sandbox/reset
Wipes all sandbox state for the client:
- Deletes every
positions_v2row withis_sandbox = true. - Clears the in-memory sandbox balance cache.
- Re-seeds with flat default balances ($1B per quote asset by default, configurable via
PUT /v1/sandbox/settings) across all known venues — including Kalshi, Polymarket, and DEX.
Response
{ "reset": true, "initial_balance_usd": 1000000000.0, "venues_seeded": 9 }PUT /v1/sandbox/settings
Set the default initial balance used by /v1/sandbox/reset. Useful when you want a different paper-portfolio size than the $1B default but don't need per-venue/per-asset granularity.
Body
{ "initial_balance_usd": 100000.0 }Response
{ "initial_balance_usd": 100000.0 }Changes take effect on the next POST /v1/sandbox/reset.
GET /v1/sandbox/settings
Returns the current default balance for the client.
{ "initial_balance_usd": 100000.0 }How sandbox and live differ
The architecture is intentionally aligned — same storage, same write path, same read path. The only divergence is the source of initial state:
| Live | Sandbox | |
|---|---|---|
| Storage | positions_v2, is_sandbox=false | positions_v2, is_sandbox=true |
| Read API | GET /v1/positions | GET /v1/positions (same, auth-filtered) |
| Fill writes | UnifiedPositionManager | UnifiedPositionManager (same) |
| Initial state | Pulled from venue REST | Configured via POST /v1/sandbox/positions |
| Order execution | Real venue submission | Adversarial sim against the real order book |
| Failure surface | Real venue rejections | Mirrors live preflight (e.g. "market buy requires a live ask") |
A sandbox order routes through the same SOR, hits the same edge process, and is dispatched to the edge's adversarial sim adapter via the per-order is_sandbox flag. Market-order safety guards (no-ask preflight) apply identically, so paper trades fail under the same conditions a live trade would fail.