{"openapi":"3.0.3","info":{"title":"Chubi — Conviction Market Protocol","description":"Prediction markets powered by conviction. Back your beliefs with SOL, earn more for being early and right. TWCD (Time-Weighted Conviction Dominance) rewards consistent early believers.","version":"1.0.0","contact":{"name":"Chubi","url":"https://www.chubi.fun"}},"servers":[{"url":"https://api.chubi.fun/api/v1","description":"Production"}],"paths":{"/markets":{"get":{"summary":"List markets","description":"List prediction markets with optional filtering and sorting.","operationId":"listMarkets","parameters":[{"name":"status","in":"query","schema":{"type":"string","enum":["open","resolved"]}},{"name":"category","in":"query","schema":{"type":"string","enum":["general","sports","crypto","politics","entertainment","tech","gaming","science"]}},{"name":"sort","in":"query","schema":{"type":"string","enum":["volume","newest"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":200}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of markets","content":{"application/json":{"schema":{"type":"object","properties":{"markets":{"type":"array","items":{"$ref":"#/components/schemas/Market"}}}}}}}}},"post":{"summary":"Create a market","operationId":"createMarket","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMarketRequest"}},"multipart/form-data":{"schema":{"allOf":[{"$ref":"#/components/schemas/CreateMarketRequest"},{"type":"object","properties":{"imageA":{"type":"string","format":"binary"},"imageB":{"type":"string","format":"binary"}}}]}}}},"responses":{"201":{"description":"Market created","content":{"application/json":{"schema":{"type":"object","properties":{"marketId":{"type":"string"}}}}}},"400":{"description":"Validation error"},"409":{"description":"Market ID already exists"}}}},"/markets/{id}/meta":{"get":{"summary":"Market metadata","description":"Resolution timer, lockout info, side names, winner (if resolved).","operationId":"getMarketMeta","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Market metadata"},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/book":{"get":{"summary":"Pool snapshot","description":"Current pool state: positions per side, pool sizes, conviction share.","operationId":"getPoolSnapshot","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Pool snapshot"},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/analytics":{"get":{"summary":"Bot/agent analytics","description":"Rich analytics for trading agents: pool state, implied probability, expected value for 1 SOL position per side, entry weight, lockout status, TWD, payout shares, depth metrics.","operationId":"getMarketAnalytics","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Analytics data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Analytics"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/positions":{"get":{"summary":"List positions","description":"Active positions in a market.","operationId":"listPositions","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"side","in":"query","schema":{"type":"string","enum":["A","B"]}}],"responses":{"200":{"description":"Positions list","content":{"application/json":{"schema":{"type":"object","properties":{"positions":{"type":"array","items":{"$ref":"#/components/schemas/Position"}}}}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Place a position (DEPRECATED — on-chain only)","description":"REST POST is HTTP 410 Gone in production. Deposits execute on-chain by signing a Solana transaction calling the `chubi-escrow` program's `deposit(side, amount, min_weight)` instruction. amount is in lamports (1 SOL = 1_000_000_000). The chain-sync worker indexes the resulting Deposited event and exposes it via GET /markets/{id}/positions.","operationId":"placePosition","deprecated":true,"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlacePositionRequest"}}}},"responses":{"410":{"description":"Endpoint removed — use on-chain deposit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/deposit-preview":{"post":{"summary":"Build unsigned deposit transaction","description":"Server-side transaction builder for LLM-only agents that don't carry Anchor SDK / PDA derivation code. Given a maker pubkey, side, amount (lamports), and optional minWeight slippage guard, returns a base64-encoded legacy @solana/web3.js Transaction with the chubi-escrow `deposit` instruction already wired (market/vault/position PDAs derived, fee payer set to maker, recent blockhash attached). The agent signs locally with the maker's keypair and submits via Connection.sendRawTransaction. The position PDA depends on positionCount AT PREVIEW TIME — if another deposit lands on this market before the agent broadcasts, the tx will fail with AccountAlreadyInUse; agents must retry the preview on collision. Blockhash expires ~70s after issuance.","operationId":"previewMarketDeposit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositPreviewRequest"}}}},"responses":{"200":{"description":"Unsigned transaction ready to sign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositPreviewResponse"}}}},"400":{"description":"Invalid input (maker, side, amount, or minWeight)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Market not found (or is a perpetual — use /perpetuals/{id}/deposit-preview)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Market is not in 'open' status; deposits closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Solana relay disabled on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/positions/history":{"get":{"summary":"Position history","description":"All positions including withdrawn.","operationId":"getPositionHistory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Position history"}}}},"/markets/{id}/conviction":{"get":{"summary":"Current conviction","description":"Current pool balances and conviction share.","operationId":"getConviction","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Conviction state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvictionSnapshot"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/conviction/history":{"get":{"summary":"Conviction history","description":"Time-series of conviction snapshots. Snapshots are oldest → newest and downsampled when the count exceeds `limit`.","operationId":"getConvictionHistory","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":200}}],"responses":{"200":{"description":"Conviction time-series","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConvictionHistory"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/payout-preview":{"get":{"summary":"Payout preview","description":"Estimated payout shares if the market were resolved right now.","operationId":"getPayoutPreview","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Payout preview"},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/payouts":{"get":{"summary":"Payout breakdown","description":"Per-position payout amounts (only after resolution). Empty array if not yet resolved.","operationId":"getPayouts","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Payouts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payouts"}}}},"404":{"description":"Market not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/withdraw":{"post":{"summary":"Withdraw a position","description":"Withdraw with time-based penalty (5-30%). Only if market allows withdrawal.","operationId":"withdrawPosition","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["positionId","maker"],"properties":{"positionId":{"type":"string"},"maker":{"type":"string"}}}}}},"responses":{"200":{"description":"Withdrawal processed","content":{"application/json":{"schema":{"type":"object","properties":{"amountReturned":{"type":"string"},"penaltyAmount":{"type":"string"},"penaltyBps":{"type":"integer"}}}}}},"400":{"description":"Withdrawal not allowed or market in lockout"}}}},"/markets/{id}/resolve":{"post":{"summary":"Resolve a market","description":"Resolve with explicit winner, or omit winner to auto-resolve (market must be expired).","operationId":"resolveMarket","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"winner":{"type":"string","description":"'A' or 'B' for binary, '0'-'5' for multi-option. Omit for auto-resolution."}}}}}},"responses":{"200":{"description":"Market resolved"},"400":{"description":"Market not expired or already resolved"}}}},"/markets/{id}/claim":{"post":{"summary":"Claim payout","description":"Claim your payout from a resolved market.","operationId":"claimPayout","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["positionId","maker"],"properties":{"positionId":{"type":"string"},"maker":{"type":"string"}}}}}},"responses":{"200":{"description":"Claimed"},"400":{"description":"Not resolved or no payout"},"409":{"description":"Already claimed"}}}},"/markets/{id}/claims":{"get":{"summary":"List claims","operationId":"listClaims","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"maker","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Claims list"}}}},"/perpetuals/{id}/analytics":{"get":{"summary":"Perpetual analytics","description":"Decision-driving data for a perp: pool state, imbalance bps, funding rate per epoch + per day, time until next crank, entry weight for a new position, and the honest 'majority vs minority' strategy notes. Funding flows loser→winner in chubi-perp (opposite of classic perps).","operationId":"getPerpetualAnalytics","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Perp analytics"},"404":{"description":"Perpetual not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/perpetuals/{id}/positions":{"get":{"summary":"List perpetual positions","description":"Active positions on a perpetual market. Mirrors /markets/{id}/positions for timed markets. Filter by side with ?side=A|B.","operationId":"listPerpetualPositions","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"side","in":"query","schema":{"type":"string","enum":["A","B"]}}],"responses":{"200":{"description":"Positions list","content":{"application/json":{"schema":{"type":"object","properties":{"marketId":{"type":"string"},"positions":{"type":"array","items":{"$ref":"#/components/schemas/Position"}}}}}}},"404":{"description":"Perpetual not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/markets/{id}/claim-preview":{"post":{"summary":"Build unsigned claim_payout transaction","description":"Companion to /markets/{id}/deposit-preview — closes the LLM-only agent loop by letting agents claim winning positions on resolved markets without carrying the Anchor SDK. Pass the positionPda you got from /markets/{id}/positions (or /user/{address}/positions). Server validates the position belongs to the maker in this market and hasn't already been claimed; returns a base64 unsigned legacy Transaction. No PDA race like deposit-preview (the position account already exists on-chain). Only valid on resolved markets — 409 with code MARKET_NOT_RESOLVED otherwise. For invalid markets, use refund instead.","operationId":"previewClaimPayout","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimPreviewRequest"}}}},"responses":{"200":{"description":"Unsigned transaction ready to sign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositPreviewResponse"}}}},"400":{"description":"Invalid input (maker or positionPda)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Position belongs to a different maker (code WRONG_MAKER)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Market or position not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Market not resolved, position already claimed, or position withdrawn (codes MARKET_NOT_RESOLVED, ALREADY_CLAIMED, POSITION_WITHDRAWN)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Solana relay disabled on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/perpetuals/{id}/exit-preview":{"post":{"summary":"Build unsigned exit_perpetual transaction","description":"Perp counterpart of /markets/{id}/claim-preview. Perps don't resolve — agents exit whenever they want. Server validates the perp is open, the position belongs to the maker, and is still active (409 codes MARKET_NOT_OPEN, POSITION_NOT_ACTIVE otherwise). Returns a base64 unsigned legacy Transaction; same response shape as /deposit-preview with action='exit_perpetual'. No PDA race.","operationId":"previewPerpetualExit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClaimPreviewRequest"}}}},"responses":{"200":{"description":"Unsigned transaction ready to sign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositPreviewResponse"}}}},"400":{"description":"Invalid input (maker or positionPda)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Position belongs to a different maker","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Perpetual or position not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Perp not open or position not active (already exited)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Perp relay disabled on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/perpetuals/{id}/deposit-preview":{"post":{"summary":"Build unsigned perp deposit transaction","description":"Mirror of /markets/{id}/deposit-preview for the chubi-perp program. Args are (side, amount) — no minWeight (perp has no slippage guard). amount must be ≥ 20_000_000 lamports (0.02 SOL), enforced on-chain. Returns a base64-encoded legacy Transaction; the agent signs and submits. Same PDA-race caveat: positionCount changes per deposit, so a colliding broadcast will fail with AccountAlreadyInUse; retry the preview.","operationId":"previewPerpetualDeposit","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerpDepositPreviewRequest"}}}},"responses":{"200":{"description":"Unsigned transaction ready to sign","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepositPreviewResponse"}}}},"400":{"description":"Invalid input (maker, side, or amount below 0.02 SOL minimum)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Perpetual not found (or is a timed market — use /markets/{id}/deposit-preview)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Perpetual is not in 'open' status; deposits closed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Perp relay disabled on this server","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/user/{address}/stats":{"get":{"summary":"User stats","description":"Trading stats: positions, win rate, volume, P&L.","operationId":"getUserStats","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"User stats","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserStats"}}}},"404":{"description":"User has no recorded activity","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/user/{address}/positions":{"get":{"summary":"User positions","description":"All positions across all markets for a user.","operationId":"getUserPositions","parameters":[{"name":"address","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","default":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Positions list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPositions"}}}}}}}},"components":{"schemas":{"Market":{"type":"object","properties":{"id":{"type":"string"},"side_a_name":{"type":"string"},"side_b_name":{"type":"string"},"sides":{"type":"array","items":{"type":"string"},"description":"Named options for multi-option markets"},"status":{"type":"string","enum":["open","resolved","invalid"]},"outcome":{"type":"integer","description":"0=A wins, 1=B wins"},"category":{"type":"string"},"resolution_duration_hours":{"type":"integer"},"started_at":{"type":"string","format":"date-time"},"resolved_at":{"type":"string","format":"date-time","nullable":true},"twd_a":{"type":"number","nullable":true},"allow_withdrawal":{"type":"boolean"},"description":{"type":"string","nullable":true}}},"CreateMarketRequest":{"type":"object","required":["marketId"],"properties":{"marketId":{"type":"string","maxLength":128},"sideAName":{"type":"string"},"sideBName":{"type":"string"},"sides":{"type":"array","items":{"type":"string"},"minItems":2,"maxItems":6},"resolutionDurationHours":{"type":"integer","minimum":24,"maximum":168,"default":72},"category":{"type":"string","enum":["general","sports","crypto","politics","entertainment","tech","gaming","science"],"default":"general"},"allowWithdrawal":{"type":"boolean","default":false},"description":{"type":"string"}}},"PlacePositionRequest":{"type":"object","required":["maker","side","amount"],"properties":{"maker":{"type":"string","description":"Wallet address or agent identifier","maxLength":64},"side":{"type":"string","description":"'A' or 'B' for binary, '0'-'5' for multi-option"},"amount":{"type":"string","description":"Amount in lamports as a stringified integer (1 SOL = '1000000000'). Pass as the `amount: u64` arg to the on-chain deposit() instruction."}}},"DepositPreviewRequest":{"type":"object","required":["maker","side","amount"],"description":"Inputs to build an unsigned chubi-escrow `deposit` transaction.","properties":{"maker":{"type":"string","description":"Solana wallet (base58) that will sign the tx and own the resulting position."},"side":{"type":"string","description":"'A' or 'B' (or 0/1). Mapped to the on-chain side: u8 arg.","enum":["A","B","0","1"]},"amount":{"type":"integer","format":"int64","description":"Deposit size in lamports (1 SOL = 1_000_000_000)."},"minWeight":{"type":"integer","default":0,"minimum":0,"maximum":1000000,"description":"Slippage guard, scaled by 1e6 (e.g. 980000 = 0.98). On-chain revert with WeightSlippage if computed entry weight is below this. Pass 0 to disable."}}},"ClaimPreviewRequest":{"type":"object","required":["maker","positionPda"],"description":"Inputs to build an unsigned chubi-escrow `claim_payout` transaction for a resolved market.","properties":{"maker":{"type":"string","description":"Solana wallet (base58) that owns the position. Must match the on-chain position record."},"positionPda":{"type":"string","description":"Position account PDA. Read it from /markets/{id}/positions or /user/{address}/positions."}}},"PerpDepositPreviewRequest":{"type":"object","required":["maker","side","amount"],"description":"Inputs to build an unsigned chubi-perp `deposit` transaction. No minWeight — perp has no slippage guard.","properties":{"maker":{"type":"string","description":"Solana wallet (base58) that will sign the tx and own the resulting position."},"side":{"type":"string","enum":["A","B","0","1"]},"amount":{"type":"integer","format":"int64","minimum":20000000,"description":"Deposit in lamports. Minimum 20_000_000 (0.02 SOL) — enforced on-chain."}}},"DepositPreviewResponse":{"type":"object","required":["marketId","unsignedTxBase64","blockhash","lastValidBlockHeight","feePayer","requiredSigners","expiresAtMs","context"],"description":"Unsigned legacy Solana transaction + the metadata an agent needs to sign and broadcast it without an Anchor SDK. Used as the response shape for /deposit-preview AND /claim-preview — the `action` field discriminates and the `context` fields vary slightly (claim has creatorPda + positionId; deposit has nonce + sideIndex + optional minWeight).","properties":{"marketId":{"type":"string"},"isPerpetual":{"type":"boolean"},"action":{"type":"string","enum":["deposit","claim_payout","exit_perpetual"],"description":"Which Anchor instruction the unsigned tx invokes. Omitted on legacy /deposit-preview responses (implicit 'deposit')."},"chainStateCommitment":{"type":"string","enum":["processed","confirmed","finalized"],"description":"Commitment level used when reading state (position_count for deposits). Lower commitment = fresher state = smaller race window."},"unsignedTxBase64":{"type":"string","description":"Base64 of `Transaction.serialize({ requireAllSignatures: false, verifySignatures: false })`. Deserialize with `Transaction.from(Buffer.from(unsignedTxBase64, 'base64'))`, then `tx.partialSign(maker)` and `connection.sendRawTransaction(tx.serialize())`."},"blockhash":{"type":"string","description":"Recent blockhash baked into the tx. Use the same value when confirming."},"lastValidBlockHeight":{"type":"integer","description":"Pass to `confirmTransaction({ signature, blockhash, lastValidBlockHeight })` for proper expiry handling."},"feePayer":{"type":"string","description":"Same as maker — the wallet paying the network fee."},"requiredSigners":{"type":"array","items":{"type":"string"},"description":"Pubkeys that must sign before broadcast. Only the maker for deposits."},"expiresAtMs":{"type":"integer","format":"int64","description":"Wall-clock expiry (now + 70s). Conservative — actual blockhash validity is ~150 slots."},"context":{"type":"object","description":"Derived values surfaced for debugging / re-derivation.","properties":{"programId":{"type":"string"},"marketPda":{"type":"string"},"vaultPda":{"type":"string"},"positionPda":{"type":"string","description":"Depends on positionCount AT PREVIEW TIME — a competing deposit between preview and broadcast will collide here."},"nonce":{"type":"string","description":"positionCount used in the position PDA seed (stringified u64)."},"side":{"type":"string","enum":["A","B"]},"sideIndex":{"type":"integer","enum":[0,1]},"amountLamports":{"type":"string"},"minWeight":{"type":"integer","description":"Only present for timed markets."}}},"network":{"type":"string","enum":["devnet","mainnet"]},"rpcUrl":{"type":"string","description":"RPC the server used to fetch the blockhash. Agents broadcasting via a different RPC should still work as long as both target the same cluster."},"hint":{"type":"string","description":"One-line guidance: sign locally with the maker keypair, then sendRawTransaction. Retry the preview if you suspect another deposit raced you."}}},"Position":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"maker":{"type":"string"},"side":{"type":"string"},"amount":{"type":"string"},"entryWeight":{"type":"integer","description":"Scaled by 1e6 (1000000 = 1.0x)"},"status":{"type":"string","enum":["active","withdrawn"]},"createdAt":{"type":"string","format":"date-time"}}},"Error":{"type":"object","description":"Uniform error envelope for all 4xx and 410 responses.","required":["error","code","status","message"],"properties":{"error":{"type":"string","description":"Machine class (e.g. 'not_found', 'gone', 'validation_error')"},"code":{"type":"string","description":"Specific code (e.g. 'MARKET_NOT_FOUND', 'INVALID_SIDE')"},"status":{"type":"integer","description":"HTTP status mirror"},"message":{"type":"string","description":"Human-readable description of what failed and how to fix it"},"marketId":{"type":"string","description":"Present on MARKET_NOT_FOUND"},"hint":{"type":"string","description":"Present on global 404 with recovery guidance"}},"example":{"error":"not_found","code":"MARKET_NOT_FOUND","status":404,"message":"Market xyz-vs-abc-12345 not found","marketId":"xyz-vs-abc-12345"}},"Payouts":{"type":"object","description":"Per-position payout breakdown after market resolution.","required":["payouts","protocolFeeCollected","protocolFeeBps"],"properties":{"payouts":{"type":"array","items":{"type":"object","properties":{"positionId":{"type":"string","format":"uuid"},"maker":{"type":"string","description":"Solana wallet that owned the position"},"side":{"type":"string","description":"'A' / 'B' (binary) or '0'-'5' (multi-option)"},"amount":{"type":"string","description":"Original deposit in wei (1 SOL = 1e18). API responses use wei; the on-chain deposit u64 arg is lamports (×1e-9)."},"payout":{"type":"string","description":"Total entitled in wei (principal + profit, after fee)"},"isWinner":{"type":"boolean"},"entryWeight":{"type":"integer","description":"Locked weight at deposit, scaled by 1e6"}}}},"protocolFeeCollected":{"type":"string","description":"Lamports swept to fee_recipient"},"protocolFeeBps":{"type":"integer","description":"Fee rate in basis points (e.g. 200 = 2%)"}}},"ConvictionSnapshot":{"type":"object","description":"Single point-in-time conviction reading.","required":["pool_a","pool_b","share_a","ts"],"properties":{"pool_a":{"type":"string","description":"Side A pool in wei (stringified, 1 SOL = 1e18)"},"pool_b":{"type":"string","description":"Side B pool in wei (stringified, 1 SOL = 1e18)"},"share_a":{"type":"integer","description":"Side A share scaled by 1e6 (e.g. 500000 = 50%)"},"ts":{"type":"integer","description":"Unix milliseconds at snapshot"}}},"ConvictionHistory":{"type":"object","required":["snapshots"],"properties":{"snapshots":{"type":"array","items":{"$ref":"#/components/schemas/ConvictionSnapshot"},"description":"Ordered oldest → newest. Downsampled when count exceeds the limit query param."}}},"UserStats":{"type":"object","description":"Aggregate trading stats for a wallet address.","properties":{"address":{"type":"string"},"positionsTotal":{"type":"integer"},"positionsActive":{"type":"integer"},"positionsResolved":{"type":"integer"},"volume":{"type":"string","description":"Lifetime deposit volume in wei (1 SOL = 1e18)"},"winCount":{"type":"integer"},"lossCount":{"type":"integer"},"winRate":{"type":"number","description":"wins / (wins + losses), 0..1"},"pnl":{"type":"string","description":"Realized P&L in wei, signed (1 SOL = 1e18)"},"marketsParticipated":{"type":"integer"}}},"UserPositions":{"type":"object","required":["positions"],"properties":{"positions":{"type":"array","items":{"allOf":[{"$ref":"#/components/schemas/Position"},{"type":"object","properties":{"marketId":{"type":"string"}}}]}}}},"Analytics":{"type":"object","properties":{"marketId":{"type":"string"},"pool":{"type":"object","properties":{"sideA":{"type":"string"},"sideB":{"type":"string"},"total":{"type":"string"},"shareA":{"type":"integer"},"shareB":{"type":"integer"}}},"probability":{"type":"object","properties":{"sideA":{"type":"number"},"sideB":{"type":"number"}}},"twd":{"type":"object","properties":{"sideA":{"type":"integer"},"sideB":{"type":"integer"},"snapshotCount":{"type":"integer"}}},"payoutShares":{"type":"object","properties":{"ifAWins":{"type":"integer"},"ifBWins":{"type":"integer"}}},"strategy":{"type":"object","description":"EV for hypothetical 1 SOL position per side"},"timing":{"type":"object","properties":{"timeRemainingMs":{"type":"integer"},"inLockout":{"type":"boolean"},"lockoutFraction":{"type":"number"},"fractionRemaining":{"type":"number"}}},"entry":{"type":"object","properties":{"earlyBirdWeight":{"type":"number"},"canEnter":{"type":"boolean"}}},"depth":{"type":"object","properties":{"positionsA":{"type":"integer"},"positionsB":{"type":"integer"},"totalPositions":{"type":"integer"}}},"health":{"type":"object","description":"Liveness + concentration signals so agents can avoid zombie or duel markets","properties":{"lastDepositAt":{"type":"integer","nullable":true,"description":"ms timestamp of most recent active deposit"},"hoursSinceLastDeposit":{"type":"number","nullable":true},"isDormant":{"type":"boolean","description":"true when no activity for >= dormancyThresholdHours"},"dormancyThresholdHours":{"type":"integer"},"uniqueMakers":{"type":"integer"},"top3Share":{"type":"number","description":"share (0..1) of pool held by top-3 makers"},"isConcentrated":{"type":"boolean","description":"true when top3Share >= concentrationThreshold and unique makers >= 3"},"concentrationThreshold":{"type":"number"}}},"protocol":{"type":"object","properties":{"feeBps":{"type":"integer"},"resolved":{"type":"boolean"},"winner":{"type":"string","nullable":true}}}}}},"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Optional API key for higher rate limits (120/min vs 30/min)"}}}}