{"openapi":"3.1.0","info":{"title":"Sports Predictor AI","description":"AI-powered sports betting analysis","version":"1.0.0"},"paths":{"/api/health":{"get":{"summary":"Health","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/quota":{"get":{"summary":"Quota","description":"How many external API calls we've used today/this month.","operationId":"quota_api_quota_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/dashboard":{"get":{"summary":"Dashboard","description":"One-stop proof + health surface: model registry, live calibration, recent backtests.","operationId":"dashboard_api_dashboard_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/scheduler/status":{"get":{"summary":"Scheduler Status","description":"Background scheduler state + last run summary.","operationId":"scheduler_status_api_scheduler_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/scheduler/run-now":{"post":{"summary":"Scheduler Run Now","description":"Manually trigger the daily model-refresh job (re-ingest intl + refit DC).","operationId":"scheduler_run_now_api_scheduler_run_now_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/probabilities":{"get":{"summary":"Probabilities Slate","description":"Every upcoming fixture with calibrated probabilities and derived markets.\n\nThe one endpoint priced for agents (x402, USDC on Base). It is pure model\noutput — no LLM — so it carries no per-call model cost, only compute.\n\nFree when X402_PAY_TO is unset, so an unconfigured deploy degrades to free\nrather than to a broken paywall.","operationId":"probabilities_slate_api_v1_probabilities_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":14,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ask":{"post":{"summary":"Ask","description":"Plain-English question -> the model's grounded answer.\n\nFixture resolution and the prediction itself are free (heuristics + math);\nonly the short write-up calls the LLM, so this sits behind the same rate\nlimits and daily budget as the other paid endpoints.","operationId":"ask_api_ask_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AskRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/fixtures":{"post":{"summary":"Get Fixtures","operationId":"get_fixtures_api_fixtures_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FixturesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/analyze":{"post":{"summary":"Analyze Match","operationId":"analyze_match_api_analyze_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MatchAnalysisRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/quick-analyze":{"post":{"summary":"Quick Analyze","operationId":"quick_analyze_api_quick_analyze_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/QuickAnalysisRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/analyze-multi":{"post":{"summary":"Analyze Multi Bet","operationId":"analyze_multi_bet_api_analyze_multi_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MultiBetRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/odds/{sport}":{"get":{"summary":"Get Odds","operationId":"get_odds_api_odds__sport__get","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/upcoming-with-odds":{"get":{"summary":"Get Upcoming With Odds","description":"Get all upcoming matches with odds across all major sports for next 7 days.","operationId":"get_upcoming_with_odds_api_upcoming_with_odds_get","parameters":[{"name":"sports","in":"query","required":false,"schema":{"type":"string","title":"Sports"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/best-bets":{"get":{"summary":"Best Bets","description":"Find the safest favourites across all sports for the next 7 days.\n\nRanks by implied probability (1/odds). No AI calls — pure odds-driven shortlist\nso it's fast and free. Use `/api/analyze` on any pick for deep analysis.","operationId":"best_bets_api_best_bets_get","parameters":[{"name":"sports","in":"query","required":false,"schema":{"type":"string","title":"Sports"}},{"name":"max_odds","in":"query","required":false,"schema":{"type":"number","default":1.6,"title":"Max Odds"}},{"name":"min_odds","in":"query","required":false,"schema":{"type":"number","default":1.18,"title":"Min Odds"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/elo/{sport}":{"get":{"summary":"Get Elo","description":"Top teams by Elo rating for the given sport.","operationId":"get_elo_api_elo__sport__get","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/elo/{sport}/predict":{"get":{"summary":"Predict Elo","description":"Quick Elo-only 1X2 prediction (no LLM, no API calls).","operationId":"predict_elo_api_elo__sport__predict_get","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"home","in":"query","required":true,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":true,"schema":{"type":"string","title":"Away"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/ensemble/weights/{sport}":{"get":{"summary":"Get Ensemble Weights","operationId":"get_ensemble_weights_api_ensemble_weights__sport__get","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Ensemble Weights","description":"Update per-sport ensemble weights. Pass weights_json as a JSON string,\ne.g. '{\"elo\": 0.5, \"poisson\": 0.4, \"llm\": 0.1}'. Components don't need to\nsum to 1.0 — blend() renormalises.","operationId":"update_ensemble_weights_api_ensemble_weights__sport__put","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"weights_json","in":"query","required":true,"schema":{"type":"string","title":"Weights Json"}},{"name":"shrinkage","in":"query","required":false,"schema":{"type":"number","default":0.0,"title":"Shrinkage"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/poisson/fit/{league_key}":{"post":{"summary":"Fit Poisson","description":"Fit Dixon-Coles on the requested league's full season and save the model.\n\nRun this once per season per league. The /analyze endpoint will then pick up\nthe saved model. ~5-10 seconds per league.","operationId":"fit_poisson_api_poisson_fit__league_key__post","parameters":[{"name":"league_key","in":"path","required":true,"schema":{"type":"string","title":"League Key"}},{"name":"season","in":"query","required":false,"schema":{"type":"integer","title":"Season"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/historical/download":{"post":{"summary":"Historical Download","description":"Download football-data.co.uk CSVs (results + closing odds) into SQLite.\n\nleagues : comma-list (default: all big-5).  seasons: comma-list like '2425,2324,2223'.\nFree, no quota impact. Run once, then use /historical/backtest.","operationId":"historical_download_api_historical_download_post","parameters":[{"name":"leagues","in":"query","required":false,"schema":{"type":"string","title":"Leagues"}},{"name":"seasons","in":"query","required":false,"schema":{"type":"string","title":"Seasons"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/historical/backtest":{"post":{"summary":"Historical Backtest","description":"Rigorous backtest on historical data WITH ROI (uses real closing odds).\n\nCompares naive/elo/poisson/market/ensembles, reporting Brier, log-loss,\nflat ROI, and value-bet ROI (only betting where we beat the closing line).","operationId":"historical_backtest_api_historical_backtest_post","parameters":[{"name":"league","in":"query","required":false,"schema":{"type":"string","default":"premier_league","title":"League"}},{"name":"seasons","in":"query","required":false,"schema":{"type":"string","title":"Seasons"}},{"name":"score_fraction","in":"query","required":false,"schema":{"type":"number","default":0.3,"title":"Score Fraction"}},{"name":"value_threshold","in":"query","required":false,"schema":{"type":"number","default":0.02,"title":"Value Threshold"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/backtest":{"post":{"summary":"Backtest","description":"Replay historical matches through chosen pipelines.\n\nQuery params:\n  leagues             : comma-list (e.g. 'premier_league,la_liga')\n  pipelines           : comma-list of {naive, elo, llm}.  'llm' costs Anthropic credits.\n  matches_per_league  : total fixtures pulled per league (default 200 ≈ full season)\n  score_fraction      : last X% scored; rest seed Elo (default 0.3 → 70% warmup)\n  score_cap           : optional max scored matches per league (use to limit LLM cost)","operationId":"backtest_api_backtest_post","parameters":[{"name":"leagues","in":"query","required":false,"schema":{"type":"string","default":"premier_league","title":"Leagues"}},{"name":"pipelines","in":"query","required":false,"schema":{"type":"string","default":"elo,naive","title":"Pipelines"}},{"name":"matches_per_league","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Matches Per League"}},{"name":"score_fraction","in":"query","required":false,"schema":{"type":"number","default":0.3,"title":"Score Fraction"}},{"name":"score_cap","in":"query","required":false,"schema":{"type":"integer","title":"Score Cap"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/bootstrap":{"post":{"summary":"Worldcup Bootstrap","description":"Seed Elo ratings for the 48 World Cup national teams.\n\nRun once before the tournament. Won't clobber ratings learned from real\nmatches unless overwrite=true.","operationId":"worldcup_bootstrap_api_worldcup_bootstrap_post","parameters":[{"name":"overwrite","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Overwrite"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/predict":{"get":{"summary":"Worldcup Predict","description":"Predict an international match using national-team Elo.\n\nneutral=true (default) strips home advantage (most WC games are neutral-venue).\nBlends with the LLM if you also want narrative context — see /analyze.","operationId":"worldcup_predict_api_worldcup_predict_get","parameters":[{"name":"home","in":"query","required":true,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":true,"schema":{"type":"string","title":"Away"}},{"name":"neutral","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Neutral"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/ingest":{"post":{"summary":"Worldcup Ingest","description":"Pull real international results (WC2022, Euro2024, Nations League, Copa,\nfriendlies) from API-Football and update national-team Elo chronologically.\nThis replaces hand-guessed seeds with data-driven ratings.","operationId":"worldcup_ingest_api_worldcup_ingest_post","parameters":[{"name":"reset_to_seeds","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Reset To Seeds"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/rebuild":{"post":{"summary":"Worldcup Rebuild","description":"Full correct rebuild of international Elo: seeds → 2022-24 competitive\n(K=1.0) → pre-WC friendlies at ⅓ weight → logged WC results at 2.5x weight.","operationId":"worldcup_rebuild_api_worldcup_rebuild_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cl/harvest":{"post":{"summary":"Cl Harvest","description":"Fetch a full CL season's results from ESPN (Tue/Wed/Thu + final dates).","operationId":"cl_harvest_api_cl_harvest_post","parameters":[{"name":"start","in":"query","required":false,"schema":{"type":"string","default":"2025-09-01","title":"Start"}},{"name":"end","in":"query","required":false,"schema":{"type":"string","default":"2026-06-05","title":"End"}},{"name":"season","in":"query","required":false,"schema":{"type":"string","default":"2025-26","title":"Season"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cl/backtest":{"post":{"summary":"Cl Backtest","description":"Walk-forward backtest on the harvested season: seed cross-league Elo\nfrom domestic ratings, predict every match with only prior info, score.","operationId":"cl_backtest_api_cl_backtest_post","parameters":[{"name":"season","in":"query","required":false,"schema":{"type":"string","default":"2025-26","title":"Season"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/cl/predict":{"get":{"summary":"Cl Predict","description":"CL fixture prediction on the walk-forward-corrected European ratings.","operationId":"cl_predict_api_cl_predict_get","parameters":[{"name":"home","in":"query","required":true,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":true,"schema":{"type":"string","title":"Away"}},{"name":"neutral","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Neutral"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clubs/refresh":{"post":{"summary":"Clubs Refresh","description":"Download latest football-data season CSVs for PL + La Liga and rebuild\neach league's Elo from all stored seasons (chronological replay).","operationId":"clubs_refresh_api_clubs_refresh_post","parameters":[{"name":"seasons","in":"query","required":false,"schema":{"type":"string","default":"2526","title":"Seasons"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clubs/backtest":{"post":{"summary":"Clubs Backtest","description":"CL-style walk-forward backtest of a domestic league season, with a\nPinnacle-closing-odds market benchmark on the same matches.","operationId":"clubs_backtest_api_clubs_backtest_post","parameters":[{"name":"league","in":"query","required":true,"schema":{"type":"string","title":"League"}},{"name":"season","in":"query","required":false,"schema":{"type":"string","default":"2526","title":"Season"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clubs/predict":{"get":{"summary":"Clubs Predict","description":"Draw-aware v2 prediction for a PL / La Liga fixture.\nleague: premier_league | la_liga. Home advantage applied.","operationId":"clubs_predict_api_clubs_predict_get","parameters":[{"name":"league","in":"query","required":true,"schema":{"type":"string","title":"League"}},{"name":"home","in":"query","required":true,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":true,"schema":{"type":"string","title":"Away"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clubs/daily-update":{"post":{"summary":"Clubs Daily Update","description":"In-season: ingest finished PL/La Liga results (ESPN, free), score the\nmodel, update league Elo. Run daily once the 2026-27 season starts.","operationId":"clubs_daily_update_api_clubs_daily_update_post","parameters":[{"name":"days_back","in":"query","required":false,"schema":{"type":"integer","default":3,"title":"Days Back"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/clubs/prelog":{"post":{"summary":"Clubs Prelog","description":"Write predictions for upcoming fixtures, before they are played.\n\nNormally the nightly scheduler does this. Exposed for the first run of a\nseason and for backfilling after downtime.","operationId":"clubs_prelog_api_clubs_prelog_post","parameters":[{"name":"days_ahead","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days Ahead"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/picks":{"post":{"summary":"Submit Pick","description":"Record a pick for an upcoming fixture. Free, no stake, no payout.\n\nRefused at or after kickoff, and refused if you already picked this\nfixture: a leaderboard where standing picks can be revised proves\nnothing.","operationId":"submit_pick_api_picks_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/picks/message":{"post":{"summary":"Pick Message","description":"The exact text to sign for a pick.\n\nServed rather than built client-side so the wallet signs the same bytes\nthe server will verify. A mismatch here is the usual way signed-payload\nschemes silently break.","operationId":"pick_message_api_picks_message_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/picks/{player}":{"get":{"summary":"Player Picks","description":"One player's picks, scored and pending.","operationId":"player_picks_api_picks__player__get","parameters":[{"name":"player","in":"path","required":true,"schema":{"type":"string","title":"Player"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/leaderboard":{"get":{"summary":"Picks Leaderboard","description":"Standings by average Brier, against the model on the same fixtures.","operationId":"picks_leaderboard_api_leaderboard_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"min_picks","in":"query","required":false,"schema":{"type":"integer","default":3,"title":"Min Picks"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/picks/score":{"post":{"summary":"Score Picks","description":"Score picks whose fixtures now have results. The scheduler does this\nnightly; exposed for backfills.","operationId":"score_picks_api_picks_score_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/predict2":{"get":{"summary":"Worldcup Predict2","description":"v2 draw-aware prediction: Elo->Poisson goals bridge + calibration layer\nfitted on our own logged WC predictions. CAN pick the draw (v1 cannot).","operationId":"worldcup_predict2_api_worldcup_predict2_get","parameters":[{"name":"home","in":"query","required":true,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":true,"schema":{"type":"string","title":"Away"}},{"name":"neutral","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Neutral"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/calibrate":{"post":{"summary":"Worldcup Calibrate","description":"(Re)fit the probability calibration layer on wc_match_log.","operationId":"worldcup_calibrate_api_worldcup_calibrate_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/daily-update":{"post":{"summary":"Worldcup Daily Update","description":"The daily ritual: ingest new WC results (ESPN, free + no quota), score the\nmodel's pre-match predictions (live calibration), update Elo at WC weight,\nand preview today/tomorrow fixtures with fresh probabilities.","operationId":"worldcup_daily_update_api_worldcup_daily_update_post","parameters":[{"name":"days_back","in":"query","required":false,"schema":{"type":"integer","default":3,"title":"Days Back"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/ratings":{"get":{"summary":"Worldcup Ratings","description":"Current national-team Elo ladder.","operationId":"worldcup_ratings_api_worldcup_ratings_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":48,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/worldcup/simulate":{"post":{"summary":"Worldcup Simulate","description":"Monte Carlo the whole tournament → advance / reach-final / win-the-cup odds.\n\ngroups_json: optional JSON of {\"A\": [\"Team1\", ...4], ...}. If omitted, the\ntop-48 rated teams are auto snake-seeded into 12 groups. Provide the REAL draw\nonce it's known for accurate outright probabilities.","operationId":"worldcup_simulate_api_worldcup_simulate_post","parameters":[{"name":"n_sims","in":"query","required":false,"schema":{"type":"integer","default":10000,"title":"N Sims"}},{"name":"seed","in":"query","required":false,"schema":{"type":"integer","title":"Seed"}},{"name":"groups_json","in":"query","required":false,"schema":{"type":"string","title":"Groups Json"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/trackrecord":{"get":{"summary":"Trackrecord","description":"The prediction log, with each row's provenance stated rather than assumed.\n\nTwo kinds of row live here and the difference matters:\n\n  prelogged=1  the probabilities were written BEFORE kickoff and the\n               result was filled in afterwards. A genuine forward\n               prediction; nothing about it can be revised in hindsight.\n  prelogged=0  the probabilities were generated when the result was\n               ingested. Still walk-forward -- the model had not seen that\n               match, and Elo updates only after the row is scored -- but\n               it is a backtest of that match, not a forward call.\n\nEvery World Cup row is prelogged=0, because pre-kickoff logging did not\nexist while that tournament was running. Saying so is the point: a record\nthat overstates how it was produced is worth less than a smaller honest\none. Club rows from the 2026-27 season onwards are pre-logged.\n\nPublic, read-only.","operationId":"trackrecord_api_trackrecord_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/calibration/reliability":{"get":{"summary":"Calibration Reliability","description":"Reliability curve + Brier decomposition for one league-season.\n\nThis is the evidence behind the calibration claim: does a stated 60%\nactually happen ~60% of the time, and how does that compare to the closing\nline on the same matches. Accuracy is deliberately not the headline — no\n1X2 model picks draws, so ~27% of matches are unwinnable on that metric.\n\nServes the report stored at fit time — a pure read. The underlying backtest\nreplays a full season AND rebuilds elo_ratings, so it must never run on a\npublic request path (CPU exhaustion + concurrent rebuilds racing the same\nratings table).","operationId":"calibration_reliability_api_calibration_reliability_get","parameters":[{"name":"league","in":"query","required":false,"schema":{"type":"string","default":"premier_league","title":"League"}},{"name":"season","in":"query","required":false,"schema":{"type":"string","default":"2526","title":"Season"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calibration/fit":{"post":{"summary":"Calibration Fit","description":"(Re)fit the per-sport calibration alphas.\n\nClub alphas come from the walk-forward backtest (no live club log yet);\nthe international alpha comes from logged World Cup predictions.","operationId":"calibration_fit_api_calibration_fit_post","parameters":[{"name":"season","in":"query","required":false,"schema":{"type":"string","default":"2526","title":"Season"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/calibration":{"get":{"summary":"Calibration","description":"How well-calibrated are the AI's confidence scores?\n\nJoins logged predictions to settled bets (by match name) and bins by confidence.\nAfter ~20+ settled bets the buckets become meaningful.","operationId":"calibration_api_calibration_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/standings/{sport}":{"get":{"summary":"Get Standings","operationId":"get_standings_api_standings__sport__get","parameters":[{"name":"sport","in":"path","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"league","in":"query","required":false,"schema":{"type":"string","title":"League"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/bets":{"post":{"summary":"Record Bet","operationId":"record_bet_api_bets_post","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetRecord"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"summary":"Get Bets","operationId":"get_bets_api_bets_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Clear Bets","operationId":"clear_bets_api_bets_delete","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/bets/{bet_id}/settle":{"put":{"summary":"Settle Bet","operationId":"settle_bet_api_bets__bet_id__settle_put","parameters":[{"name":"bet_id","in":"path","required":true,"schema":{"type":"integer","title":"Bet Id"}},{"name":"result","in":"query","required":true,"schema":{"type":"string","title":"Result"}},{"name":"actual_score","in":"query","required":false,"schema":{"type":"string","title":"Actual Score"}},{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/bets/summary":{"get":{"summary":"Bet Summary","operationId":"bet_summary_api_bets_summary_get","parameters":[{"name":"x-admin-token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Admin-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"AskRequest":{"properties":{"question":{"type":"string","maxLength":200,"minLength":3,"title":"Question"}},"type":"object","required":["question"],"title":"AskRequest"},"BetRecord":{"properties":{"match":{"type":"string","title":"Match"},"bet_type":{"type":"string","title":"Bet Type"},"pick":{"type":"string","title":"Pick"},"odds":{"type":"number","title":"Odds"},"stake":{"type":"number","title":"Stake"},"result":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Result","default":"pending"},"actual_score":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actual Score"}},"type":"object","required":["match","bet_type","pick","odds","stake"],"title":"BetRecord"},"FixturesRequest":{"properties":{"sport":{"type":"string","title":"Sport"},"league":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"League"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"}},"type":"object","required":["sport"],"title":"FixturesRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"MatchAnalysisRequest":{"properties":{"sport":{"type":"string","title":"Sport"},"league":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"League"},"home_team":{"type":"string","title":"Home Team"},"away_team":{"type":"string","title":"Away Team"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"venue":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Venue"},"extra_context":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Extra Context"}},"type":"object","required":["sport","home_team","away_team"],"title":"MatchAnalysisRequest"},"MultiBetLeg":{"properties":{"match":{"type":"string","title":"Match"},"pick":{"type":"string","title":"Pick"},"odds":{"type":"number","title":"Odds"},"sport":{"type":"string","title":"Sport"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence"}},"type":"object","required":["match","pick","odds","sport"],"title":"MultiBetLeg"},"MultiBetRequest":{"properties":{"legs":{"items":{"$ref":"#/components/schemas/MultiBetLeg"},"type":"array","title":"Legs"},"stake":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stake"},"bankroll":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Bankroll"}},"type":"object","required":["legs"],"title":"MultiBetRequest"},"QuickAnalysisRequest":{"properties":{"query":{"type":"string","title":"Query"}},"type":"object","required":["query"],"title":"QuickAnalysisRequest"},"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"}}}}