{"openapi":"3.0.3","info":{"title":"NT Predictions Market API","description":"REST API for the NT Connect Predictions Market — NBA & MLB event contracts. Manage sports events, binary prediction markets, order placement, positions, and user wallets.","version":"1.0.0","contact":{"name":"NT Predictions Market","email":"support@ntpredictions.io"},"license":{"name":"MIT"}},"servers":[{"url":"http://localhost:3000","description":"Local development"}],"tags":[{"name":"Auth","description":"Authentication — register, login, logout, refresh, password"},{"name":"Users","description":"User profile management and admin operations"},{"name":"Events","description":"Sports events (CBB & NBA games)"},{"name":"Markets","description":"Prediction markets tied to events"},{"name":"Contracts","description":"Tradable YES/NO instruments with order books"},{"name":"Orders","description":"Place, list, and cancel orders"},{"name":"Fills","description":"Trade execution records (public trade tape)"},{"name":"Positions","description":"User holdings and P&L"},{"name":"Wallets","description":"User cash balances, deposits, withdrawals"},{"name":"Health","description":"Service health checks"},{"name":"Admin","description":"Admin-only platform operations (requires admin role)"}],"security":[{"BearerAuth":[]}],"paths":{"/api/auth/register":{"post":{"tags":["Auth"],"summary":"Register a new user","description":"Creates user account + wallet, returns JWT tokens.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","username","password"],"properties":{"email":{"type":"string","format":"email"},"username":{"type":"string","minLength":3,"maxLength":50},"password":{"type":"string","minLength":8}}}}}},"responses":{"201":{"description":"User created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"400":{"description":"Validation failed"},"409":{"description":"Email or username already taken"}}}},"/api/auth/login":{"post":{"tags":["Auth"],"summary":"Login","description":"Authenticate with email + password, returns JWT tokens.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","password"],"properties":{"email":{"type":"string","format":"email"},"password":{"type":"string"}}}}}},"responses":{"200":{"description":"Authenticated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid credentials"},"403":{"description":"Account deactivated"}}}},"/api/auth/refresh":{"post":{"tags":["Auth"],"summary":"Refresh tokens","description":"Exchange a refresh token for new access + refresh tokens.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["refreshToken"],"properties":{"refreshToken":{"type":"string"}}}}}},"responses":{"200":{"description":"Tokens refreshed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthResponse"}}}},"401":{"description":"Invalid or expired refresh token"}}}},"/api/auth/me":{"get":{"tags":["Auth"],"summary":"Current user profile","description":"Returns the authenticated user's profile and wallet.","responses":{"200":{"description":"User profile with wallet"},"401":{"description":"Not authenticated"}}}},"/api/auth/logout":{"post":{"tags":["Auth"],"summary":"Logout","description":"Blacklists the current access token and optionally a refresh token.","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"refreshToken":{"type":"string","description":"Optional refresh token to also invalidate"}}}}}},"responses":{"200":{"description":"Logged out successfully"},"401":{"description":"Not authenticated"}}}},"/api/auth/change-password":{"post":{"tags":["Auth"],"summary":"Change password","description":"Change the authenticated user's password.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["currentPassword","newPassword"],"properties":{"currentPassword":{"type":"string"},"newPassword":{"type":"string","minLength":8}}}}}},"responses":{"200":{"description":"Password changed"},"401":{"description":"Current password incorrect"}}}},"/api/users/me":{"get":{"tags":["Users"],"summary":"Get own profile","responses":{"200":{"description":"User profile with wallet"}}},"patch":{"tags":["Users"],"summary":"Update own profile","requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string","minLength":3,"maxLength":50},"email":{"type":"string","format":"email"}}}}}},"responses":{"200":{"description":"Updated profile"},"409":{"description":"Username or email already taken"}}}},"/api/users":{"get":{"tags":["Users"],"summary":"List all users (admin)","description":"Admin only. List users with search and filters.","parameters":[{"name":"role","in":"query","schema":{"type":"string","enum":["user","admin"]}},{"name":"is_active","in":"query","schema":{"type":"boolean"}},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by email or username"},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated user list with total count"},"403":{"description":"Admin access required"}}}},"/api/users/{id}":{"get":{"tags":["Users"],"summary":"Get user by ID (admin)","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"User details with wallet, position & order counts"},"404":{"description":"User not found"}}},"patch":{"tags":["Users"],"summary":"Update user (admin)","description":"Admin only. Change role or active status.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["user","admin"]},"is_active":{"type":"boolean"}}}}}},"responses":{"200":{"description":"Updated user"},"403":{"description":"Admin access required"},"404":{"description":"User not found"}}}},"/api/fills":{"get":{"tags":["Fills"],"summary":"List fills (trade tape)","description":"Public trade tape. Filter by contract_id or market_id.","security":[],"parameters":[{"name":"contract_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"market_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"user_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Requires auth; own fills or admin"},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated fills with total count"}}}},"/api/fills/{id}":{"get":{"tags":["Fills"],"summary":"Get fill by ID","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Fill details"},"404":{"description":"Fill not found"}}}},"/api/contracts":{"get":{"tags":["Contracts"],"summary":"List contracts","description":"Search/filter contracts independently of markets.","security":[],"parameters":[{"name":"market_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"side","in":"query","schema":{"type":"string","enum":["YES","NO"]}},{"name":"status","in":"query","schema":{"type":"string","enum":["active","halted","settled"]}},{"name":"search","in":"query","schema":{"type":"string"},"description":"Search by symbol"},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Paginated contracts with total count"}}}},"/api/contracts/{id}":{"get":{"tags":["Contracts"],"summary":"Get contract with order book","description":"Single contract with order book snapshot, recent trades, and open interest.","security":[],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Contract with order_book, recent_trades, open_interest"},"404":{"description":"Contract not found"}}}},"/api/health":{"get":{"tags":["Health"],"summary":"Health check","description":"Returns service health status including database and Redis connectivity.","operationId":"getHealth","responses":{"200":{"description":"Service is healthy","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","format":"date-time"},"postgres":{"type":"string","example":"connected"},"redis":{"type":"string","example":"connected"}}}}}}}}},"/api/events":{"get":{"tags":["Events"],"summary":"List sports events","description":"Retrieve sports events with optional filters for sport type and status.","operationId":"listEvents","parameters":[{"name":"sport","in":"query","schema":{"type":"string","enum":["CBB","NBA"]},"description":"Filter by sport"},{"name":"status","in":"query","schema":{"type":"string","enum":["scheduled","live","completed","cancelled"]},"description":"Filter by event status"},{"name":"limit","in":"query","schema":{"type":"integer","default":50,"maximum":100}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Event"}},"count":{"type":"integer"}}}}}}}},"post":{"tags":["Events"],"summary":"Create a sports event","description":"Create a new CBB or NBA game event.","operationId":"createEvent","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventRequest"}}}},"responses":{"201":{"description":"Event created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/api/events/{id}":{"get":{"tags":["Events"],"summary":"Get event by ID","description":"Retrieve a single event including its markets.","operationId":"getEvent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Event with markets","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Events"],"summary":"Update an event","operationId":"updateEvent","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEventRequest"}}}},"responses":{"200":{"description":"Updated event","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Event"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/markets":{"get":{"tags":["Markets"],"summary":"List prediction markets","operationId":"listMarkets","parameters":[{"name":"event_id","in":"query","schema":{"type":"string","format":"uuid"},"description":"Filter by event"},{"name":"status","in":"query","schema":{"type":"string","enum":["open","suspended","closed","settled"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of markets","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Market"}},"count":{"type":"integer"}}}}}}}},"post":{"tags":["Markets"],"summary":"Create a prediction market","description":"Create a new binary prediction market under an event. Auto-generates YES and NO contracts.","operationId":"createMarket","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMarketRequest"}}}},"responses":{"201":{"description":"Market with contracts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Market"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/markets/{id}":{"get":{"tags":["Markets"],"summary":"Get market by ID","operationId":"getMarket","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Market with contracts","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Market"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Markets"],"summary":"Update a market","description":"Update market fields. Set status=settled and settlement_price to settle.","operationId":"updateMarket","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMarketRequest"}}}},"responses":{"200":{"description":"Updated market","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Market"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/orders":{"get":{"tags":["Orders"],"summary":"List orders","operationId":"listOrders","parameters":[{"name":"user_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"contract_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"status","in":"query","schema":{"type":"string","enum":["open","partial","filled","cancelled"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of orders","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Order"}},"count":{"type":"integer"}}}}}}}},"post":{"tags":["Orders"],"summary":"Place an order","description":"Place a buy or sell order on a binary contract. Limit price must be 0.01–0.99.","operationId":"placeOrder","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlaceOrderRequest"}}}},"responses":{"201":{"description":"Order placed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/orders/{id}":{"get":{"tags":["Orders"],"summary":"Get order by ID","operationId":"getOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Order with fills","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"404":{"$ref":"#/components/responses/NotFound"}}},"patch":{"tags":["Orders"],"summary":"Modify an open order","description":"Modify the price and/or quantity of an open or partially filled order. Adjusts reserved funds accordingly.","operationId":"modifyOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"price":{"type":"number","minimum":0.01,"maximum":0.99,"description":"New limit price"},"qty":{"type":"integer","minimum":1,"description":"New total quantity (must be > filled_qty)"}}}}}},"responses":{"200":{"description":"Modified order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"},"503":{"description":"Trading is halted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"delete":{"tags":["Orders"],"summary":"Cancel an order","description":"Cancel an open or partially filled order. Releases reserved funds.","operationId":"cancelOrder","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Order"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/orders/book/{contract_id}":{"get":{"tags":["Orders"],"summary":"Get order book","description":"Aggregated bids and asks for a contract. Cached in Redis (2s TTL).","operationId":"getOrderBook","parameters":[{"name":"contract_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Order book","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrderBook"}}}}}}},"/api/positions":{"get":{"tags":["Positions"],"summary":"List user positions","operationId":"listPositions","parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"contract_id","in":"query","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"List of positions with unrealized P&L","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Position"}},"count":{"type":"integer"}}}}}},"400":{"$ref":"#/components/responses/BadRequest"}}}},"/api/positions/{id}":{"get":{"tags":["Positions"],"summary":"Get position by ID","operationId":"getPosition","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Position with fills","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Position"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/positions/summary/{user_id}":{"get":{"tags":["Positions"],"summary":"Portfolio summary","description":"Aggregate portfolio summary: total value, realized and unrealized P&L.","operationId":"getPortfolioSummary","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Portfolio summary","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioSummary"}}}}}}},"/api/wallets/{user_id}":{"get":{"tags":["Wallets"],"summary":"Get user wallet","operationId":"getWallet","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}}},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/wallets":{"post":{"tags":["Wallets"],"summary":"Create a wallet","operationId":"createWallet","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["user_id"],"properties":{"user_id":{"type":"string","format":"uuid"}}}}}},"responses":{"201":{"description":"Wallet created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}}},"409":{"description":"Wallet already exists"}}}},"/api/wallets/{user_id}/deposit":{"post":{"tags":["Wallets"],"summary":"Deposit funds","operationId":"deposit","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletTransactionRequest"}}}},"responses":{"200":{"description":"Updated wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/wallets/{user_id}/withdraw":{"post":{"tags":["Wallets"],"summary":"Withdraw funds","operationId":"withdraw","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WalletTransactionRequest"}}}},"responses":{"200":{"description":"Updated wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wallet"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"404":{"$ref":"#/components/responses/NotFound"}}}},"/api/wallets/{user_id}/transactions":{"get":{"tags":["Wallets"],"summary":"List wallet transactions","operationId":"listWalletTransactions","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}},{"name":"type","in":"query","schema":{"type":"string","enum":["deposit","withdrawal","order_reserve","order_release","trade_debit","trade_credit","settlement"]}},{"name":"limit","in":"query","schema":{"type":"integer","default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}}],"responses":{"200":{"description":"Transaction history","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/WalletTransaction"}},"count":{"type":"integer"}}}}}}}}},"/api/admin/metrics":{"get":{"tags":["Admin"],"summary":"Platform-wide metrics","operationId":"getAdminMetrics","responses":{"200":{"description":"Aggregated platform metrics (users, markets, orders, fills, wallets)"}}}},"/api/admin/halt":{"post":{"tags":["Admin"],"summary":"Halt all trading","operationId":"haltTrading","responses":{"200":{"description":"Trading halted","content":{"application/json":{"schema":{"type":"object","properties":{"halted":{"type":"boolean"},"message":{"type":"string"}}}}}}}}},"/api/admin/resume":{"post":{"tags":["Admin"],"summary":"Resume trading","operationId":"resumeTrading","responses":{"200":{"description":"Trading resumed","content":{"application/json":{"schema":{"type":"object","properties":{"halted":{"type":"boolean"},"message":{"type":"string"}}}}}}}}},"/api/admin/trading-status":{"get":{"tags":["Admin"],"summary":"Check trading halt state","operationId":"getTradingStatus","responses":{"200":{"description":"Current trading status","content":{"application/json":{"schema":{"type":"object","properties":{"halted":{"type":"boolean"}}}}}}}}},"/api/admin/sync":{"post":{"tags":["Admin"],"summary":"Trigger Kalshi event/market/contract sync","operationId":"triggerSync","responses":{"200":{"description":"Sync results with stats"}}}},"/api/admin/settle-sync":{"post":{"tags":["Admin"],"summary":"Trigger settlement status check","operationId":"triggerSettleSync","responses":{"200":{"description":"Settlement sync results"}}}},"/api/admin/njt-accounts":{"get":{"tags":["Admin"],"summary":"List users with NJT provisioning status","operationId":"listNjtAccounts","responses":{"200":{"description":"List of users with NJT account info"}}}},"/api/admin/njt-provision/{userId}":{"post":{"tags":["Admin"],"summary":"Manually provision NJT account for a user","operationId":"provisionNjtAccount","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Provisioning result"}}}},"/api/admin/reset-account/{userId}":{"post":{"tags":["Admin"],"summary":"Reset user positions, orders, and wallet reserves","operationId":"resetAccount","parameters":[{"name":"userId","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Reset result"}}}},"/api/admin/bridge-stats":{"get":{"tags":["Admin"],"summary":"Get bridge process stats","operationId":"getBridgeStats","responses":{"200":{"description":"Bridge statistics including trading halt, contract count, open markets"}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"AuthResponse":{"type":"object","properties":{"accessToken":{"type":"string"},"refreshToken":{"type":"string"},"user":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"email":{"type":"string","format":"email"},"username":{"type":"string"},"role":{"type":"string","enum":["user","admin"]}}}}},"Event":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"sport":{"type":"string","enum":["CBB","NBA"]},"external_id":{"type":"string","nullable":true},"title":{"type":"string"},"description":{"type":"string","nullable":true},"home_team":{"type":"string","nullable":true},"away_team":{"type":"string","nullable":true},"starts_at":{"type":"string","format":"date-time","nullable":true},"status":{"type":"string","enum":["scheduled","live","completed","cancelled"]},"metadata":{"type":"object","nullable":true},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CreateEventRequest":{"type":"object","required":["sport","title"],"properties":{"sport":{"type":"string","enum":["CBB","NBA"]},"title":{"type":"string","example":"Duke vs UNC"},"description":{"type":"string"},"home_team":{"type":"string"},"away_team":{"type":"string"},"starts_at":{"type":"string","format":"date-time"},"metadata":{"type":"object"}}},"UpdateEventRequest":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["scheduled","live","completed","cancelled"]},"starts_at":{"type":"string","format":"date-time"},"home_team":{"type":"string"},"away_team":{"type":"string"},"metadata":{"type":"object"}}},"Market":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"event_id":{"type":"string","format":"uuid"},"title":{"type":"string"},"description":{"type":"string","nullable":true},"market_type":{"type":"string","enum":["binary","spread","over_under"]},"status":{"type":"string","enum":["open","suspended","closed","settled"]},"settlement_price":{"type":"number","nullable":true},"closes_at":{"type":"string","format":"date-time","nullable":true},"settled_at":{"type":"string","format":"date-time","nullable":true},"contracts":{"type":"array","items":{"$ref":"#/components/schemas/Contract"}},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"CreateMarketRequest":{"type":"object","required":["event_id","title"],"properties":{"event_id":{"type":"string","format":"uuid"},"title":{"type":"string","example":"Will Duke win?"},"description":{"type":"string"},"market_type":{"type":"string","default":"binary"},"closes_at":{"type":"string","format":"date-time"}}},"UpdateMarketRequest":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"status":{"type":"string","enum":["open","suspended","closed","settled"]},"settlement_price":{"type":"number","description":"1.0 = Yes wins, 0.0 = No wins"},"closes_at":{"type":"string","format":"date-time"}}},"Contract":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"market_id":{"type":"string","format":"uuid"},"symbol":{"type":"string","example":"CBB-DUKE-WIN-YES"},"side":{"type":"string","enum":["YES","NO"]},"last_price":{"type":"number","nullable":true},"best_bid":{"type":"number","nullable":true},"best_ask":{"type":"number","nullable":true},"volume_24h":{"type":"integer"},"status":{"type":"string","enum":["active","halted","settled"]},"settlement_value":{"type":"number","nullable":true}}},"Order":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"contract_id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["buy","sell"]},"order_type":{"type":"string","enum":["limit","market"]},"price":{"type":"number","nullable":true,"description":"0.01–0.99 for binary"},"qty":{"type":"integer"},"filled_qty":{"type":"integer"},"remaining_qty":{"type":"integer"},"status":{"type":"string","enum":["open","partial","filled","cancelled"]},"time_in_force":{"type":"string","enum":["GTC","DAY","IOC","FOK"]},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"cancelled_at":{"type":"string","format":"date-time","nullable":true}}},"PlaceOrderRequest":{"type":"object","required":["contract_id","side","qty"],"properties":{"contract_id":{"type":"string","format":"uuid"},"side":{"type":"string","enum":["buy","sell"]},"order_type":{"type":"string","enum":["limit","market"],"default":"limit"},"price":{"type":"number","description":"Required for limit orders. Range: 0.01–0.99"},"qty":{"type":"integer","minimum":1},"time_in_force":{"type":"string","enum":["GTC","DAY","IOC","FOK"],"default":"GTC"}}},"OrderBook":{"type":"object","properties":{"contract_id":{"type":"string","format":"uuid"},"bids":{"type":"array","items":{"type":"object","properties":{"price":{"type":"number"},"total_qty":{"type":"integer"}}}},"asks":{"type":"array","items":{"type":"object","properties":{"price":{"type":"number"},"total_qty":{"type":"integer"}}}}}},"Position":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"contract_id":{"type":"string","format":"uuid"},"symbol":{"type":"string"},"qty":{"type":"integer"},"avg_price":{"type":"number"},"realized_pnl":{"type":"number"},"unrealized_pnl":{"type":"number"},"market_title":{"type":"string"},"market_status":{"type":"string"}}},"PortfolioSummary":{"type":"object","properties":{"user_id":{"type":"string","format":"uuid"},"open_positions":{"type":"integer"},"total_cost":{"type":"number"},"total_market_value":{"type":"number"},"total_unrealized_pnl":{"type":"number"},"total_realized_pnl":{"type":"number"}}},"Wallet":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"balance":{"type":"number","description":"Available cash"},"reserved":{"type":"number","description":"Locked in open orders"},"currency":{"type":"string","default":"USD"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"}}},"WalletTransactionRequest":{"type":"object","required":["amount"],"properties":{"amount":{"type":"number","minimum":0.01},"description":{"type":"string"}}},"WalletTransaction":{"type":"object","properties":{"id":{"type":"string","format":"uuid"},"wallet_id":{"type":"string","format":"uuid"},"user_id":{"type":"string","format":"uuid"},"type":{"type":"string","enum":["deposit","withdrawal","order_reserve","order_release","trade_debit","trade_credit","settlement"]},"amount":{"type":"number"},"balance_after":{"type":"number"},"reference_id":{"type":"string","format":"uuid","nullable":true},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}}},"responses":{"BadRequest":{"description":"Bad request — validation failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}