{"openapi":"3.1.0","info":{"title":"Stock Trading Advisor","description":"AI-powered Indian stock market day trading advisor","version":"1.0.0"},"paths":{"/api/recommendations":{"get":{"tags":["recommendations"],"summary":"Get Recommendations","operationId":"get_recommendations_api_recommendations_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"symbol","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Symbol"}},{"name":"today_only","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Today Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RecommendationResponse"},"title":"Response Get Recommendations Api Recommendations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/recommendations/active":{"get":{"tags":["recommendations"],"summary":"Get Active Recommendations","operationId":"get_active_recommendations_api_recommendations_active_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RecommendationResponse"},"type":"array","title":"Response Get Active Recommendations Api Recommendations Active Get"}}}}}}},"/api/recommendations/{reco_id}/execute":{"post":{"tags":["recommendations"],"summary":"Mark Executed","description":"Mark a recommendation as executed and create a position.\n\nIDEMPOTENT: a second execute for the same recommendation does NOT create\na second Position. UI double-clicks and network retries used to silently\ncreate duplicate positions linked to the same reco — the trader thought\nit was a refresh, but the broker may have honoured both. Now we look up\nthe existing position by recommendation_id and return it as-is.","operationId":"mark_executed_api_recommendations__reco_id__execute_post","parameters":[{"name":"reco_id","in":"path","required":true,"schema":{"type":"integer","title":"Reco Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExecuteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/recommendations/{reco_id}/dismiss":{"post":{"tags":["recommendations"],"summary":"Dismiss Recommendation","operationId":"dismiss_recommendation_api_recommendations__reco_id__dismiss_post","parameters":[{"name":"reco_id","in":"path","required":true,"schema":{"type":"integer","title":"Reco Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watchlist":{"get":{"tags":["watchlist"],"summary":"Get Watchlist","description":"Get all watchlist stocks with latest prices.","operationId":"get_watchlist_api_watchlist_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/watchlist/add/{symbol}":{"post":{"tags":["watchlist"],"summary":"Add To Watchlist","operationId":"add_to_watchlist_api_watchlist_add__symbol__post","parameters":[{"name":"symbol","in":"path","required":true,"schema":{"type":"string","title":"Symbol"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watchlist/remove/{symbol}":{"delete":{"tags":["watchlist"],"summary":"Remove From Watchlist","operationId":"remove_from_watchlist_api_watchlist_remove__symbol__delete","parameters":[{"name":"symbol","in":"path","required":true,"schema":{"type":"string","title":"Symbol"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/watchlist/custom":{"get":{"tags":["watchlist"],"summary":"Get Custom Symbols","description":"Get only the user-added symbols (not part of Nifty 50).","operationId":"get_custom_symbols_api_watchlist_custom_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/watchlist/search":{"get":{"tags":["watchlist"],"summary":"Search Nse Symbols","description":"Search NSE symbols by company name or code.\n\nReturns up to 10 matching equity symbols with their company names.\nUses NSE autocomplete API with Redis caching (5 min per query).","operationId":"search_nse_symbols_api_watchlist_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","title":"Q"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/portfolio/positions":{"get":{"tags":["portfolio"],"summary":"Get Open Positions","operationId":"get_open_positions_api_portfolio_positions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/portfolio/pnl/today":{"get":{"tags":["portfolio"],"summary":"Get Today Pnl","operationId":"get_today_pnl_api_portfolio_pnl_today_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/portfolio/pnl/history":{"get":{"tags":["portfolio"],"summary":"Get Pnl History","operationId":"get_pnl_history_api_portfolio_pnl_history_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/portfolio/positions/add":{"post":{"tags":["portfolio"],"summary":"Add Position","description":"Manually log a position you've taken.","operationId":"add_position_api_portfolio_positions_add_post","parameters":[{"name":"symbol","in":"query","required":true,"schema":{"type":"string","title":"Symbol"}},{"name":"entry_price","in":"query","required":true,"schema":{"type":"number","title":"Entry Price"}},{"name":"quantity","in":"query","required":true,"schema":{"type":"integer","title":"Quantity"}},{"name":"side","in":"query","required":false,"schema":{"type":"string","default":"LONG","title":"Side"}},{"name":"is_mtf","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Is Mtf"}},{"name":"target_price","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Target Price"}},{"name":"stop_loss","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stop Loss"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/portfolio/positions/{position_id}/close":{"post":{"tags":["portfolio"],"summary":"Close Position","description":"Idempotent + race-safe close.\n\nTwo concurrent calls used to overwrite each other's realized_pnl\n(read-modify-write race) — second writer wins regardless of correctness.\nPhase-1 audit reproduced: parallel closes at $120 / $80 produced\n{200.0, -200.0} from the same position.\n\nDefense: compare-and-swap UPDATE — the database itself decides who wins.\n`UPDATE ... WHERE status='open'` returns rowcount=1 for the winner and 0\nfor everyone else. The losers re-read, see the winning realized_pnl, and\nreturn it idempotently. Works identically on Postgres and SQLite,\nindependent of isolation level.","operationId":"close_position_api_portfolio_positions__position_id__close_post","parameters":[{"name":"position_id","in":"path","required":true,"schema":{"type":"integer","title":"Position Id"}},{"name":"exit_price","in":"query","required":true,"schema":{"type":"number","title":"Exit Price"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/market/sentiment":{"get":{"tags":["market"],"summary":"Get Market Sentiment","operationId":"get_market_sentiment_api_market_sentiment_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/global":{"get":{"tags":["market"],"summary":"Get Global Indices","operationId":"get_global_indices_api_market_global_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/news":{"get":{"tags":["market"],"summary":"Get News","operationId":"get_news_api_market_news_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/market/premarket":{"get":{"tags":["market"],"summary":"Get Premarket Report","operationId":"get_premarket_report_api_market_premarket_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/sectors":{"get":{"tags":["market"],"summary":"Get Sector Performance","operationId":"get_sector_performance_api_market_sectors_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/config/min-spread":{"get":{"tags":["market"],"summary":"Get Min Spread","description":"Get the current minimum target spread % for recommendations.","operationId":"get_min_spread_api_market_config_min_spread_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["market"],"summary":"Set Min Spread","description":"Set the minimum target spread % for recommendations.","operationId":"set_min_spread_api_market_config_min_spread_post","parameters":[{"name":"min_spread_pct","in":"query","required":true,"schema":{"type":"number","title":"Min Spread Pct"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/market/risk":{"get":{"tags":["market"],"summary":"Get Risk Status","operationId":"get_risk_status_api_market_risk_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/ai-status":{"get":{"tags":["market"],"summary":"Get Ai Status","operationId":"get_ai_status_api_market_ai_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/market/readiness":{"get":{"tags":["market"],"summary":"Get Readiness","description":"Readiness snapshot. Wrapped in a top-level try/except so a transient\nDB/Redis fault — or a schema-drift bug like the missing-`source`-column\nincident — does NOT 500 the dashboard. The UI's TimelineStrip used to\nswallow the 500 silently and display \"Checking platform readiness…\"\nforever. Now we always return 200 with at least a session label, and\nsurface the underlying error so /health and the dashboard can flag it.","operationId":"get_readiness_api_market_readiness_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/simulate/start":{"post":{"tags":["simulate"],"summary":"Start Simulation","description":"Start streaming simulated market data.\n\n- duration_minutes: how long to run (default 5)\n- speed: 1.0 = realistic pace (tick every 1-3s), 5.0 = 5x faster","operationId":"start_simulation_api_simulate_start_post","parameters":[{"name":"duration_minutes","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Duration Minutes"}},{"name":"speed","in":"query","required":false,"schema":{"type":"number","default":1.0,"title":"Speed"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/simulate/stop":{"post":{"tags":["simulate"],"summary":"Stop Simulation","description":"Stop the running simulation.","operationId":"stop_simulation_api_simulate_stop_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/simulate/status":{"get":{"tags":["simulate"],"summary":"Simulation Status","description":"Check if simulation is running.","operationId":"simulation_status_api_simulate_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/simulate/recommendation":{"post":{"tags":["simulate"],"summary":"Trigger Recommendation","description":"Push a single mock recommendation to the dashboard.","operationId":"trigger_recommendation_api_simulate_recommendation_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/simulate/test-pipeline":{"post":{"tags":["simulate"],"summary":"Test Pipeline","description":"Run the REAL recommendation pipeline on synthetic data.\n\nExercises: candle ingestion → indicator computation → scanner triggers\n→ context building → AI recommendation → composite scoring → risk validation.\n\nWorks outside market hours. Uses isolated engine instances (no live state contamination).","operationId":"test_pipeline_api_simulate_test_pipeline_post","parameters":[{"name":"symbols","in":"query","required":false,"schema":{"type":"string","default":"RELIANCE,TCS,INFY","title":"Symbols"}},{"name":"use_mock_ai","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Use Mock Ai"}},{"name":"save_to_db","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Save To Db"}},{"name":"broadcast_ws","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Broadcast Ws"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/login-url":{"get":{"tags":["auth"],"summary":"Get Login Url","description":"Return the ICICI Direct login URL for session token generation.\n\nAlso surfaces `token_age_seconds` and `token_expires_in_seconds` so\nthe frontend can show how stale the current Breeze session is —\nBreeze tokens expire 24h after issue, so an operator's daily routine\nis: \"if age > 18h, rotate before market opens.\"","operationId":"get_login_url_api_auth_login_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/token-status":{"get":{"tags":["auth"],"summary":"Get Token Status","description":"Lightweight endpoint for the topbar to poll without re-fetching\nthe full login URL. Returns just the age / expires info.\n\nNo auth-protected secrets are revealed by this endpoint — only\ntiming data. Cached by the frontend with a short TTL (60s) so the\ntopbar pill updates without hammering the backend.","operationId":"get_token_status_api_auth_token_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/callback":{"get":{"tags":["auth"],"summary":"Auth Callback","description":"Capture the session token from ICICI redirect (GET or POST).","operationId":"auth_callback_api_auth_callback_get","parameters":[{"name":"apisession","in":"query","required":false,"schema":{"type":"string","default":"","title":"Apisession"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["auth"],"summary":"Auth Callback","description":"Capture the session token from ICICI redirect (GET or POST).","operationId":"auth_callback_api_auth_callback_get","parameters":[{"name":"apisession","in":"query","required":false,"schema":{"type":"string","default":"","title":"Apisession"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/set-token":{"post":{"tags":["auth"],"summary":"Set Token","description":"Manually set the Breeze session token. Validates with Breeze before saving.\n\nSingle authoritative endpoint — the prior duplicate dict-body variant\nbypassed validation and logged the full token in plaintext.","operationId":"set_token_api_auth_set_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/learning/report/today":{"get":{"tags":["learning"],"summary":"Get Today Report","description":"Get today's daily report card.","operationId":"get_today_report_api_learning_report_today_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/learning/report/history":{"get":{"tags":["learning"],"summary":"Get Report History","description":"Get report cards for the last N days.","operationId":"get_report_history_api_learning_report_history_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/learning/journal/recent":{"get":{"tags":["learning"],"summary":"Get Recent Journal","description":"Get recent AI journal entries.","operationId":"get_recent_journal_api_learning_journal_recent_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/learning/ml/status":{"get":{"tags":["learning"],"summary":"Get Ml Status","description":"Get ML model status.","operationId":"get_ml_status_api_learning_ml_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/learning/stats":{"get":{"tags":["learning"],"summary":"Get Learning Stats","description":"Aggregate learning stats.","operationId":"get_learning_stats_api_learning_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user-auth/login":{"post":{"tags":["user-auth"],"summary":"Login","operationId":"login_api_user_auth_login_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoginRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user-auth/logout":{"post":{"tags":["user-auth"],"summary":"Logout","operationId":"logout_api_user_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user-auth/me":{"get":{"tags":["user-auth"],"summary":"Get Me","operationId":"get_me_api_user_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user-auth/refresh":{"post":{"tags":["user-auth"],"summary":"Refresh Session","description":"Sliding-session refresh: re-issue the access token for a live session.\n\nThe dashboard calls this every ~30 min while the tab is open (and on\ntab-visible after machine wake), so an operator who logs in at night\nstays signed in for the 08:30 premarket kick-start without the base\nTTL having to be enormous. Requires a currently-valid token — an\nexpired session cannot resurrect itself, and anonymous callers get the\nusual 401 from get_current_user.\n\nRevocation caveat (same as login): tokens carry a jti but no blacklist\nis wired yet, so refresh extends a stolen-but-unexpired token too.\nAcceptable for this operator-scale platform; the jti groundwork exists\nfor when a blacklist lands.","operationId":"refresh_session_api_user_auth_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user-auth/register":{"post":{"tags":["user-auth"],"summary":"Register","operationId":"register_api_user_auth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user-auth/users":{"get":{"tags":["user-auth"],"summary":"List Users","operationId":"list_users_api_user_auth_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/user-auth/users/{user_id}":{"put":{"tags":["user-auth"],"summary":"Update User","operationId":"update_user_api_user_auth_users__user_id__put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["user-auth"],"summary":"Delete User","operationId":"delete_user_api_user_auth_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user-auth/users/{user_id}/password":{"post":{"tags":["user-auth"],"summary":"Change User Password","description":"Admin-only: set any user's password (including the admin's own).\n\nEnforces the same complexity rules as scripts/ops/reset_password.py so\noperators can't accidentally weaken accounts via the UI. The new\npassword is bcrypt-hashed before storage; the plaintext never lives\nlonger than this request handler.\n\nNote on session invalidation: changing a password does NOT currently\ninvalidate existing JWTs for the target user. Tokens are short-lived\n(via settings.jwt_expiry_hours) so the practical exposure window is\nbounded, but a stolen token will continue to work until natural\nexpiry. Adding a `password_version` column + JWT claim is the standard\nnext-step hardening; left as a follow-up to keep this PR small.","operationId":"change_user_password_api_user_auth_users__user_id__password_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/user-auth/ws-token":{"get":{"tags":["user-auth"],"summary":"Get Ws Token","description":"Return a short-lived JWT (60 seconds) for WebSocket authentication.","operationId":"get_ws_token_api_user_auth_ws_token_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/admin/auto-tune/run":{"post":{"tags":["admin"],"summary":"Auto Tune Run","description":"Manually trigger the nightly auto-tuner.\n\nUseful for verification immediately after a backfill (so the tuner sees\nfresh labels) and for resuming after a safety pause has been resolved.","operationId":"auto_tune_run_api_admin_auto_tune_run_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Auto Tune Run Api Admin Auto Tune Run Post"}}}}}}},"/api/admin/auto-tune/resume":{"post":{"tags":["admin"],"summary":"Auto Tune Resume","description":"Clear the auto-tuner pause flag set by the safety circuit.","operationId":"auto_tune_resume_api_admin_auto_tune_resume_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Auto Tune Resume Api Admin Auto Tune Resume Post"}}}}}}},"/api/admin/eod/backfill":{"post":{"tags":["admin"],"summary":"Eod Backfill","description":"Force the EOD evaluator to run a backfill pass NOW.\n\nUsed to cure stuck `active` recs from prior days that slipped through the\ndaily 15:20 IST loop (e.g., when the evaluator raised before the fix that\ntaught it to use realized Breeze candles instead of emission-time\nsnapshots). Safe to call any time — the evaluator is idempotent.","operationId":"eod_backfill_api_admin_eod_backfill_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Eod Backfill Api Admin Eod Backfill Post"}}}}}}},"/api/admin/shutdown":{"post":{"tags":["admin"],"summary":"Shutdown","description":"Gracefully stop the backend (and frontend via the kill script).\n\nRequires admin role (raises 403 otherwise — mirrors every other\nadmin-gated endpoint in this project).","operationId":"shutdown_api_admin_shutdown_post","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Shutdown Api Admin Shutdown Post"}}}}}}},"/api/admin/rejection-funnel":{"get":{"tags":["admin"],"summary":"Rejection Funnel","description":"Per-stage gate rejection histogram.\n\nQuery: ?date=YYYY-MM-DD (omit for today). Returns counts of attempts\nthat were rejected at each stage plus the most common reason — the\noperator-facing surface for \"which gate killed everything today?\".","operationId":"rejection_funnel_api_admin_rejection_funnel_get","parameters":[{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Rejection Funnel Api Admin Rejection Funnel Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/shadow-vs-live":{"get":{"tags":["admin"],"summary":"Shadow Vs Live","description":"Recent shadow_comparison rows.\n\nQuery: ?days=N (default 5). Used during canary rollout to verify\nshadow HR exceeds live HR by >=10pp before flipping the pipeline\nflag.","operationId":"shadow_vs_live_api_admin_shadow_vs_live_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":5,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Shadow Vs Live Api Admin Shadow Vs Live Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/universe":{"get":{"tags":["long-term"],"summary":"List Universe","description":"Paginated list of all scored companies for the latest score_date.","operationId":"list_universe_api_long_term_universe_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"per_page","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Per Page"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","pattern":"^(score_aware|score_strict|rank|market_cap)$","default":"score_aware","title":"Sort"}},{"name":"sort_dir","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","default":"desc","title":"Sort Dir"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by ISIN, NSE symbol, or company-name substring","title":"Q"},"description":"Filter by ISIN, NSE symbol, or company-name substring"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UniverseListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/top":{"get":{"tags":["long-term"],"summary":"Get Top","description":"Today's top names with thesis previews.","operationId":"get_top_api_long_term_top_get","parameters":[{"name":"n","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"N"}},{"name":"min_score","in":"query","required":false,"schema":{"type":"number","maximum":1.0,"minimum":0.0,"default":0.75,"title":"Min Score"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TopScoreItem"},"title":"Response Get Top Api Long Term Top Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/company/{isin}":{"get":{"tags":["long-term"],"summary":"Get Company Detail","operationId":"get_company_detail_api_long_term_company__isin__get","parameters":[{"name":"isin","in":"path","required":true,"schema":{"type":"string","title":"Isin"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompanyDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/company/{isin}/history":{"get":{"tags":["long-term"],"summary":"Get Company History","operationId":"get_company_history_api_long_term_company__isin__history_get","parameters":[{"name":"isin","in":"path","required":true,"schema":{"type":"string","title":"Isin"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":730,"minimum":7,"default":180,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HistoryPoint"},"title":"Response Get Company History Api Long Term Company  Isin  History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/rules":{"get":{"tags":["long-term"],"summary":"Get Rule Definitions","description":"Static list of all 40 rules — for the frontend tooltip / legend.","operationId":"get_rule_definitions_api_long_term_rules_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"additionalProperties":{"type":"string"},"type":"object"},"type":"array","title":"Response Get Rule Definitions Api Long Term Rules Get"}}}}}}},"/api/long-term/holdings":{"get":{"tags":["long-term"],"summary":"List Holdings","operationId":"list_holdings_api_long_term_holdings_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(active|under_review|exited)$"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/HoldingResponse"},"title":"Response List Holdings Api Long Term Holdings Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["long-term"],"summary":"Create Holding","operationId":"create_holding_api_long_term_holdings_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateHoldingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/holdings/{holding_id}":{"patch":{"tags":["long-term"],"summary":"Update Holding","operationId":"update_holding_api_long_term_holdings__holding_id__patch","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"integer","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateHoldingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/holdings/{holding_id}/exit":{"post":{"tags":["long-term"],"summary":"Exit Holding","operationId":"exit_holding_api_long_term_holdings__holding_id__exit_post","parameters":[{"name":"holding_id","in":"path","required":true,"schema":{"type":"integer","title":"Holding Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExitHoldingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HoldingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/alerts":{"get":{"tags":["long-term"],"summary":"List Alerts","operationId":"list_alerts_api_long_term_alerts_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","pattern":"^(open|all)$","default":"open","title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AlertResponse"},"title":"Response List Alerts Api Long Term Alerts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/alerts/{alert_id}/acknowledge":{"post":{"tags":["long-term"],"summary":"Acknowledge Alert","operationId":"acknowledge_alert_api_long_term_alerts__alert_id__acknowledge_post","parameters":[{"name":"alert_id","in":"path","required":true,"schema":{"type":"integer","title":"Alert Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcknowledgeAlertRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlertResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/admin/rescore":{"post":{"tags":["long-term"],"summary":"Admin Rescore","description":"Trigger the daily orchestrator in the background. Admin only.\n\nReturns IMMEDIATELY with status='started' — the actual work runs\nafter the HTTP response is sent (FastAPI BackgroundTasks). For a\n~900-name universe with provider rate-limit and Claude theses for\nthe top 25, the run takes 5-15 minutes. Poll /admin/last-run to\nsee progress + final result.\n\nReturns 409 if another rescore is already running.","operationId":"admin_rescore_api_long_term_admin_rescore_post","parameters":[{"name":"bust_cache","in":"query","required":false,"schema":{"type":"boolean","description":"Flush longterm:screener:* Redis keys before scoring. Use after deploying a ScreenerProvider parser change so the new code produces fresh payloads instead of re-serving the prior shape from the 24h cache.","default":false,"title":"Bust Cache"},"description":"Flush longterm:screener:* Redis keys before scoring. Use after deploying a ScreenerProvider parser change so the new code produces fresh payloads instead of re-serving the prior shape from the 24h cache."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RescoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/admin/upload-fundamentals":{"post":{"tags":["long-term"],"summary":"Admin Upload Fundamentals","description":"Upload an operator-curated Excel sheet of fundamentals.\n\nThe expected layout matches the user's `Static Algorithm.xlsx`:\n    - Sheet 'Acedata'      — fundamentals (column B = ISIN)\n    - Sheet 'Screener data' — ownership, 5yr CF, industry PE (col D = ISIN)\n    - Sheet 'Sheet6'       — universe ID mapping (col E = ISIN)\n    - Sheet 'Sheet7'       — director remuneration aggregator\n\nAny subset of those sheets works — missing ones are skipped. Acedata\nis the only one required for fundamentals to land.\n\nSide effects:\n    - Upserts `long_term_universe` rows for every ISIN in Sheet6\n    - Replaces (DELETE+INSERT) `fundamental_snapshots` for today's date\n      for every ISIN in Acedata, marking provider='excel_upload'\n    - If rescore=True, runs the scorer against the new snapshots and\n      ranks them","operationId":"admin_upload_fundamentals_api_long_term_admin_upload_fundamentals_post","parameters":[{"name":"rescore","in":"query","required":false,"schema":{"type":"boolean","description":"Queue a rescore in background after import","default":true,"title":"Rescore"},"description":"Queue a rescore in background after import"}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_admin_upload_fundamentals_api_long_term_admin_upload_fundamentals_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Upload Fundamentals Api Long Term Admin Upload Fundamentals Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/long-term/admin/last-run":{"get":{"tags":["long-term"],"summary":"Admin Last Run","description":"Return the most-recent run summary by inspecting persisted data.\n\nAlso surfaces in-flight background rescore state from _rescore_state\nso the dashboard can show \"rescore running…\" while the cron is doing\nits 5-15 minute job.","operationId":"admin_last_run_api_long_term_admin_last_run_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Admin Last Run Api Long Term Admin Last Run Get"}}}}}}},"/api/sme":{"get":{"tags":["sme"],"summary":"List Companies","description":"List all SME companies, sorted recommended-first then by PAT desc.","operationId":"list_companies_api_sme_get","parameters":[{"name":"exchange","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"BSE_SME or NSE_EMERGE; omit for both","title":"Exchange"},"description":"BSE_SME or NSE_EMERGE; omit for both"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SMECompanyResponse"},"title":"Response List Companies Api Sme Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sme/thresholds":{"get":{"tags":["sme"],"summary":"Get Thresholds","operationId":"get_thresholds_api_sme_thresholds_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMEThresholdResponse"}}}}}},"put":{"tags":["sme"],"summary":"Update Thresholds","operationId":"update_thresholds_api_sme_thresholds_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMEThresholdUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMEThresholdResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sme/refresh":{"post":{"tags":["sme"],"summary":"Refresh","description":"Kick off a comprehensive refresh run.\n\nRuns every SME data source in sequence:\n\n  Stage 1 — Exchange API enrichment (~3-5 min, Semaphore(10))\n    BSE ComHeadernew + StockTrading, NSE quote-equity (two variants).\n    Fills PE / full market_cap / volume for 100% of listed names.\n\n  Stage 2 — Screener fundamentals (~40-50 min, Cloudflare-paced)\n    Fills Revenue / EBITDA / PAT / promoter_holding.\n\nReturns immediately with a job_id; poll GET /sme/refresh/{job_id} for\nstatus + per-stage progress (exposed under ``result.stages``).","operationId":"refresh_api_sme_refresh_post","parameters":[{"name":"only_missing","in":"query","required":false,"schema":{"type":"boolean","description":"If true, the Screener stage skips companies that already have all three Screener-sourced fundamentals (pat/pe/promoter). The Exchange-API stage still runs (it's cheap, ~3-5 min) and refreshes PE + full market_cap for every row.","default":false,"title":"Only Missing"},"description":"If true, the Screener stage skips companies that already have all three Screener-sourced fundamentals (pat/pe/promoter). The Exchange-API stage still runs (it's cheap, ~3-5 min) and refreshes PE + full market_cap for every row."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMERefreshResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sme/backfill-market-cap":{"post":{"tags":["sme"],"summary":"Backfill Market Cap","description":"Refresh only the market_cap column from the cheap universe APIs.\n\nUseful right after the 2026-05-21 migration that added the column —\nthe standard ``only_missing=True`` resume path skips already-complete\nrows and therefore won't pick up the new field. This endpoint takes\n~10-30s vs. the 40-50min full scrape.\n\nReturns immediately with a job_id; poll GET /sme/refresh/{job_id}.","operationId":"backfill_market_cap_api_sme_backfill_market_cap_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMERefreshResponse"}}}}}}},"/api/sme/refresh/{job_id}":{"get":{"tags":["sme"],"summary":"Refresh Status","description":"Poll the status of an SME refresh job. Returns 404 once the TTL\n(~24h) has elapsed and the registry has expired the record.\n\nThe ``progress`` field carries per-stage markers from the comprehensive\nrefresh orchestrator (stage names + counts), so the UI can show\n\"stage 1/2: exchange enrichment (340/993)\" without tail-ing logs.","operationId":"refresh_status_api_sme_refresh__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SMEJobStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sme/export":{"get":{"tags":["sme"],"summary":"Export Excel","operationId":"export_excel_api_sme_export_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/risk-settings/me":{"get":{"tags":["risk-settings"],"summary":"My Profile","description":"Return the effective risk profile for the logged-in user.\n\nSame shape every caller can rely on — global fallbacks are pre-applied\nso the response always carries a fully-populated set of thresholds.","operationId":"my_profile_api_risk_settings_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskProfileResponse"}}}}}}},"/api/risk-settings/{user_id}":{"get":{"tags":["risk-settings"],"summary":"Admin Get Profile","operationId":"admin_get_profile_api_risk_settings__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["risk-settings"],"summary":"Admin Upsert Profile","description":"Create or update a per-user risk-settings row.\n\nPass `null` (or omit) any field to revert it to the global default.\nThe effective profile is recomputed after the write and returned to\nthe caller so the UI can refresh without a second round-trip.","operationId":"admin_upsert_profile_api_risk_settings__user_id__put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskSettingsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["risk-settings"],"summary":"Admin Clear Profile","description":"Delete the per-user override; subsequent risk checks fall back to\nthe global Settings defaults for this user.","operationId":"admin_clear_profile_api_risk_settings__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RiskProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/login-url":{"get":{"tags":["auth"],"summary":"Get Login Url","description":"Return the ICICI Direct login URL for session token generation.\n\nAlso surfaces `token_age_seconds` and `token_expires_in_seconds` so\nthe frontend can show how stale the current Breeze session is —\nBreeze tokens expire 24h after issue, so an operator's daily routine\nis: \"if age > 18h, rotate before market opens.\"","operationId":"get_login_url_auth_login_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/token-status":{"get":{"tags":["auth"],"summary":"Get Token Status","description":"Lightweight endpoint for the topbar to poll without re-fetching\nthe full login URL. Returns just the age / expires info.\n\nNo auth-protected secrets are revealed by this endpoint — only\ntiming data. Cached by the frontend with a short TTL (60s) so the\ntopbar pill updates without hammering the backend.","operationId":"get_token_status_auth_token_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/callback":{"get":{"tags":["auth"],"summary":"Auth Callback","description":"Capture the session token from ICICI redirect (GET or POST).","operationId":"auth_callback_auth_callback_get","parameters":[{"name":"apisession","in":"query","required":false,"schema":{"type":"string","default":"","title":"Apisession"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["auth"],"summary":"Auth Callback","description":"Capture the session token from ICICI redirect (GET or POST).","operationId":"auth_callback_auth_callback_get","parameters":[{"name":"apisession","in":"query","required":false,"schema":{"type":"string","default":"","title":"Apisession"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/auth/set-token":{"post":{"tags":["auth"],"summary":"Set Token","description":"Manually set the Breeze session token. Validates with Breeze before saving.\n\nSingle authoritative endpoint — the prior duplicate dict-body variant\nbypassed validation and logged the full token in plaintext.","operationId":"set_token_auth_set_token_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetTokenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/debug/engine-state":{"get":{"summary":"Debug Engine State","description":"Check tech engine candle counts and indicator availability.","operationId":"debug_engine_state_debug_engine_state_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health Check","description":"Real probe — returns 503 if any critical dep is down or any\nsupervised loop is dead. Monitoring should page on non-200.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/debug/engine":{"get":{"summary":"Debug Engine","description":"Temporary debug endpoint to check tech engine state.","operationId":"debug_engine_debug_engine_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AcknowledgeAlertRequest":{"properties":{"action":{"type":"string","enum":["exit","hold","dismiss"],"title":"Action"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["action"],"title":"AcknowledgeAlertRequest"},"AlertResponse":{"properties":{"id":{"type":"integer","title":"Id"},"holding_id":{"type":"integer","title":"Holding Id"},"alert_date":{"type":"string","format":"date","title":"Alert Date"},"alert_type":{"type":"string","title":"Alert Type"},"reason_text":{"type":"string","title":"Reason Text"},"trigger_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Trigger Data"},"acknowledged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Acknowledged At"},"acknowledgment_action":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Acknowledgment Action"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","holding_id","alert_date","alert_type","reason_text","trigger_data","acknowledged_at","acknowledgment_action","created_at"],"title":"AlertResponse"},"Body_admin_upload_fundamentals_api_long_term_admin_upload_fundamentals_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_admin_upload_fundamentals_api_long_term_admin_upload_fundamentals_post"},"CategoryScore":{"properties":{"passed":{"type":"integer","title":"Passed"},"applicable":{"type":"integer","title":"Applicable"},"total":{"type":"integer","title":"Total"},"pct":{"type":"number","title":"Pct"}},"type":"object","required":["passed","applicable","total","pct"],"title":"CategoryScore"},"ChangePasswordRequest":{"properties":{"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["new_password"],"title":"ChangePasswordRequest"},"CompanyDetailResponse":{"properties":{"isin":{"type":"string","title":"Isin"},"nse_symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nse Symbol"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"sector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"business_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Description"},"snapshot_date":{"type":"string","format":"date","title":"Snapshot Date"},"score_strict":{"type":"number","title":"Score Strict"},"score_aware":{"type":"number","title":"Score Aware"},"passed_count":{"type":"integer","title":"Passed Count"},"applicable_count":{"type":"integer","title":"Applicable Count"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank"},"category_scores":{"additionalProperties":{"$ref":"#/components/schemas/CategoryScore"},"type":"object","title":"Category Scores"},"rules":{"items":{"$ref":"#/components/schemas/RuleEvaluation"},"type":"array","title":"Rules"},"fundamentals":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"},{"type":"null"}]},"type":"object","title":"Fundamentals"},"thesis":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Thesis"}},"type":"object","required":["isin","nse_symbol","company_name","sector","industry","business_description","snapshot_date","score_strict","score_aware","passed_count","applicable_count","rank","category_scores","rules","fundamentals","thesis"],"title":"CompanyDetailResponse"},"CreateHoldingRequest":{"properties":{"isin":{"type":"string","maxLength":20,"minLength":10,"title":"Isin"},"nse_symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nse Symbol"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"entry_price":{"type":"number","exclusiveMinimum":0.0,"title":"Entry Price"},"quantity":{"type":"integer","exclusiveMinimum":0.0,"title":"Quantity"},"hold_target_months":{"type":"integer","maximum":120.0,"minimum":1.0,"title":"Hold Target Months","default":36},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["isin","entry_date","entry_price","quantity"],"title":"CreateHoldingRequest"},"ExecuteRequest":{"properties":{"quantity":{"type":"integer","title":"Quantity"},"entry_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Entry Price"}},"type":"object","required":["quantity"],"title":"ExecuteRequest"},"ExitHoldingRequest":{"properties":{"exit_date":{"type":"string","format":"date","title":"Exit Date"},"exit_price":{"type":"number","exclusiveMinimum":0.0,"title":"Exit Price"}},"type":"object","required":["exit_date","exit_price"],"title":"ExitHoldingRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HistoryPoint":{"properties":{"score_date":{"type":"string","format":"date","title":"Score Date"},"score_aware":{"type":"number","title":"Score Aware"},"score_strict":{"type":"number","title":"Score Strict"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank"}},"type":"object","required":["score_date","score_aware","score_strict","rank"],"title":"HistoryPoint"},"HoldingResponse":{"properties":{"id":{"type":"integer","title":"Id"},"user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"User Id"},"isin":{"type":"string","title":"Isin"},"nse_symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nse Symbol"},"entry_date":{"type":"string","format":"date","title":"Entry Date"},"entry_price":{"type":"number","title":"Entry Price"},"quantity":{"type":"integer","title":"Quantity"},"cost_basis":{"type":"number","title":"Cost Basis"},"entry_thesis_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Entry Thesis Id"},"hold_target_months":{"type":"integer","title":"Hold Target Months"},"status":{"type":"string","title":"Status"},"exit_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Exit Date"},"exit_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Exit Price"},"realized_pnl":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Realized Pnl"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"current_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Price"},"unrealized_pnl":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Unrealized Pnl"},"current_score_aware":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Score Aware"},"open_alert_count":{"type":"integer","title":"Open Alert Count","default":0}},"type":"object","required":["id","user_id","isin","nse_symbol","entry_date","entry_price","quantity","cost_basis","entry_thesis_id","hold_target_months","status","exit_date","exit_price","realized_pnl","notes","created_at","updated_at"],"title":"HoldingResponse"},"LoginRequest":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"LoginRequest"},"RecommendationResponse":{"properties":{"id":{"type":"integer","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"symbol":{"type":"string","title":"Symbol"},"action":{"type":"string","title":"Action"},"entry_price":{"type":"number","title":"Entry Price"},"target_price":{"type":"number","title":"Target Price"},"stop_loss":{"type":"number","title":"Stop Loss"},"confidence":{"type":"number","title":"Confidence"},"risk_reward_ratio":{"type":"number","title":"Risk Reward Ratio"},"position_size_pct":{"type":"number","title":"Position Size Pct"},"reasoning":{"type":"string","title":"Reasoning"},"timeframe":{"type":"string","title":"Timeframe"},"status":{"type":"string","title":"Status"},"risk_check_passed":{"type":"boolean","title":"Risk Check Passed"},"risk_check_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Risk Check Notes"},"day_high":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Day High"},"day_low":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Day Low"},"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tier"},"composite_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Composite Score"},"trigger_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Trigger Count","default":0},"triggers_fired":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Triggers Fired"},"market_regime":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Market Regime"},"timeframe_alignment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timeframe Alignment"},"oi_support":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Oi Support"},"oi_resistance":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Oi Resistance"}},"type":"object","required":["id","timestamp","symbol","action","entry_price","target_price","stop_loss","confidence","risk_reward_ratio","position_size_pct","reasoning","timeframe","status","risk_check_passed"],"title":"RecommendationResponse"},"RegisterRequest":{"properties":{"email":{"type":"string","title":"Email"},"username":{"type":"string","title":"Username"},"password":{"type":"string","title":"Password"},"display_name":{"type":"string","title":"Display Name","default":""},"role":{"type":"string","title":"Role","default":"viewer"}},"type":"object","required":["email","username","password"],"title":"RegisterRequest"},"RescoreResponse":{"properties":{"status":{"type":"string","title":"Status"},"summary":{"additionalProperties":true,"type":"object","title":"Summary"}},"type":"object","required":["status","summary"],"title":"RescoreResponse"},"RiskProfileResponse":{"properties":{"user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"User Id"},"is_custom":{"type":"boolean","title":"Is Custom"},"total_capital":{"type":"number","title":"Total Capital"},"max_loss_per_trade_pct":{"type":"number","title":"Max Loss Per Trade Pct"},"daily_loss_limit_pct":{"type":"number","title":"Daily Loss Limit Pct"},"max_position_size_pct":{"type":"number","title":"Max Position Size Pct"},"max_position_size_abs":{"type":"number","title":"Max Position Size Abs"},"max_sector_exposure_pct":{"type":"number","title":"Max Sector Exposure Pct"},"max_open_positions":{"type":"integer","title":"Max Open Positions"},"max_daily_capital_deployment_pct":{"type":"number","title":"Max Daily Capital Deployment Pct"},"emergency_halt_drawdown_pct":{"type":"number","title":"Emergency Halt Drawdown Pct"},"no_new_entry_after":{"type":"string","title":"No New Entry After"},"force_exit_by":{"type":"string","title":"Force Exit By"}},"type":"object","required":["user_id","is_custom","total_capital","max_loss_per_trade_pct","daily_loss_limit_pct","max_position_size_pct","max_position_size_abs","max_sector_exposure_pct","max_open_positions","max_daily_capital_deployment_pct","emergency_halt_drawdown_pct","no_new_entry_after","force_exit_by"],"title":"RiskProfileResponse","description":"Effective profile — every field always populated."},"RiskSettingsUpdate":{"properties":{"total_capital":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Total Capital"},"max_loss_per_trade_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Max Loss Per Trade Pct"},"daily_loss_limit_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Daily Loss Limit Pct"},"max_position_size_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Max Position Size Pct"},"max_position_size_abs":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Max Position Size Abs"},"max_sector_exposure_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Max Sector Exposure Pct"},"max_open_positions":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Max Open Positions"},"max_daily_capital_deployment_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Max Daily Capital Deployment Pct"},"emergency_halt_drawdown_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Emergency Halt Drawdown Pct"},"no_new_entry_after":{"anyOf":[{"type":"string","pattern":"^\\d{2}:\\d{2}$"},{"type":"null"}],"title":"No New Entry After"},"force_exit_by":{"anyOf":[{"type":"string","pattern":"^\\d{2}:\\d{2}$"},{"type":"null"}],"title":"Force Exit By"}},"type":"object","title":"RiskSettingsUpdate","description":"All fields optional — NULL/omitted means \"use the global default."},"RuleEvaluation":{"properties":{"key":{"type":"string","title":"Key"},"excel_col":{"type":"string","title":"Excel Col"},"category":{"type":"string","title":"Category"},"description":{"type":"string","title":"Description"},"outcome":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Outcome"}},"type":"object","required":["key","excel_col","category","description","outcome"],"title":"RuleEvaluation"},"SMECompanyResponse":{"properties":{"id":{"type":"integer","title":"Id"},"symbol":{"type":"string","title":"Symbol"},"exchange":{"type":"string","title":"Exchange"},"company_name":{"type":"string","title":"Company Name"},"revenue":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Revenue"},"ebitda":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ebitda"},"pat":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pat"},"pe":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pe"},"volume":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Volume"},"promoter_holding":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Promoter Holding"},"market_cap":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Cap"},"listing_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Listing Date"},"lifetime_high":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lifetime High"},"lifetime_low":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lifetime Low"},"current_value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Value"},"price_3m_trend":{"anyOf":[{"items":{"type":"number"},"type":"array"},{"type":"null"}],"title":"Price 3M Trend"},"last_scraped_at":{"type":"string","format":"date-time","title":"Last Scraped At"},"scrape_status":{"type":"string","title":"Scrape Status"},"scrape_errors":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Scrape Errors"},"is_recommended":{"type":"boolean","title":"Is Recommended"}},"type":"object","required":["id","symbol","exchange","company_name","last_scraped_at","scrape_status","is_recommended"],"title":"SMECompanyResponse"},"SMEJobStatusResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"started_at":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Finished At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"result":{"anyOf":[{"additionalProperties":true,"type":"object"},{"items":{},"type":"array"},{"type":"string"},{"type":"integer"},{"type":"number"},{"type":"boolean"},{"type":"null"}],"title":"Result"},"progress":{"additionalProperties":{"type":"string"},"type":"object","title":"Progress"}},"type":"object","required":["id","name","status"],"title":"SMEJobStatusResponse"},"SMERefreshResponse":{"properties":{"status":{"type":"string","title":"Status"},"message":{"type":"string","title":"Message"},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Job Id"}},"type":"object","required":["status","message"],"title":"SMERefreshResponse"},"SMEThresholdResponse":{"properties":{"pat_min_cr":{"type":"number","title":"Pat Min Cr"},"pe_max":{"type":"number","title":"Pe Max"},"promoter_min_pct":{"type":"number","title":"Promoter Min Pct"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"updated_by_user_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Updated By User Id"}},"type":"object","required":["pat_min_cr","pe_max","promoter_min_pct","updated_at"],"title":"SMEThresholdResponse"},"SMEThresholdUpdate":{"properties":{"pat_min_cr":{"type":"number","maximum":10000.0,"minimum":0.0,"title":"Pat Min Cr"},"pe_max":{"type":"number","maximum":1000.0,"exclusiveMinimum":0.0,"title":"Pe Max"},"promoter_min_pct":{"type":"number","maximum":100.0,"minimum":0.0,"title":"Promoter Min Pct"}},"type":"object","required":["pat_min_cr","pe_max","promoter_min_pct"],"title":"SMEThresholdUpdate"},"SetTokenRequest":{"properties":{"token":{"type":"string","title":"Token"}},"type":"object","required":["token"],"title":"SetTokenRequest"},"TopScoreItem":{"properties":{"isin":{"type":"string","title":"Isin"},"nse_symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nse Symbol"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"sector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"},"industry":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Industry"},"market_cap":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Cap"},"current_price":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Current Price"},"score_strict":{"type":"number","title":"Score Strict"},"score_aware":{"type":"number","title":"Score Aware"},"passed_count":{"type":"integer","title":"Passed Count"},"applicable_count":{"type":"integer","title":"Applicable Count"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank"},"prev_rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Prev Rank"},"rank_change":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank Change"},"thesis_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thesis Summary"},"conviction":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conviction"},"price_target_1yr":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Target 1Yr"},"price_target_3yr":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Target 3Yr"}},"type":"object","required":["isin","nse_symbol","company_name","sector","industry","market_cap","current_price","score_strict","score_aware","passed_count","applicable_count","rank","prev_rank","rank_change","thesis_summary","conviction","price_target_1yr","price_target_3yr"],"title":"TopScoreItem","description":"One row in the GET /top endpoint."},"UniverseListItem":{"properties":{"isin":{"type":"string","title":"Isin"},"nse_symbol":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nse Symbol"},"company_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Name"},"sector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sector"},"market_cap":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Market Cap"},"score_aware":{"type":"number","title":"Score Aware"},"score_strict":{"type":"number","title":"Score Strict"},"rank":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Rank"},"passed_count":{"type":"integer","title":"Passed Count"},"applicable_count":{"type":"integer","title":"Applicable Count"}},"type":"object","required":["isin","nse_symbol","company_name","sector","market_cap","score_aware","score_strict","rank","passed_count","applicable_count"],"title":"UniverseListItem"},"UniverseListResponse":{"properties":{"score_date":{"type":"string","format":"date","title":"Score Date"},"total":{"type":"integer","title":"Total"},"page":{"type":"integer","title":"Page"},"per_page":{"type":"integer","title":"Per Page"},"items":{"items":{"$ref":"#/components/schemas/UniverseListItem"},"type":"array","title":"Items"}},"type":"object","required":["score_date","total","page","per_page","items"],"title":"UniverseListResponse"},"UpdateHoldingRequest":{"properties":{"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"hold_target_months":{"anyOf":[{"type":"integer","maximum":120.0,"minimum":1.0},{"type":"null"}],"title":"Hold Target Months"},"status":{"anyOf":[{"type":"string","enum":["active","under_review","exited"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"UpdateHoldingRequest"},"UpdateUserRequest":{"properties":{"is_active":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Active"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"}},"type":"object","title":"UpdateUserRequest"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}