{"ok":true,"data":{"api_version":"v1","base_url":"https://spartanboxing.ai/api/v1","interactive_docs_url":"https://spartanboxing.ai/api-docs","auth":{"scheme":"Bearer token in Authorization header (or X-API-Key header)","env_var":"API_TOKEN","example":"Authorization: Bearer <token>","public_endpoints":["/api/v1/health","/api/v1/policy","/api/v1/docs"]},"response_envelope":{"success":{"ok":true,"data":"<endpoint-specific>","meta":{"generated_at":"ISO timestamp","api_version":"v1","model_version":"lgbm_ens_v5_YYYY-MM-DD (when applicable)","cron_age_minutes":"minutes since last successful predict-live cron"}},"error":{"ok":false,"error":{"code":"snake_case","message":"human-readable"},"meta":{"generated_at":"ISO","api_version":"v1"}}},"polymarket_integration_notes":{"pricing_units":"Polymarket prices in USDC per share, equivalent to implied probability in [0, 1]. Use `implied_prob_raw` (or `implied_prob_vigfree` for sportsbook-style markets) from each pick instead of decimal_odds. Decimal odds and American odds are also surfaced on optimizer endpoints (`decimal_odds` + `american_odds`) for traders syncing across both pricing conventions.","mapping":"fight_id is internal to this system. To trade on Polymarket, maintain your own fight_id ↔ Polymarket condition_id mapping. The fighter_a / fighter_b strings + fight_date should be enough to match. Use /api/v1/fighters?search=name to look up canonical fighter_ids.","latency":"Live cron runs every 5 min; freshness reported in meta.cron_age_minutes. Poll at any cadence — the underlying data refreshes at most 5 minutes stale.","edge_decay":"Median CLV decays ~1pp per hour as the close approaches. The `best_market.clv_vs_close` field on settled picks quantifies historical alpha decay. Bet quickly when BET appears.","stake_cap_warning":"max_bet_pct=0 removes the audit-derived 3% cap. Useful for low-Kelly bankrolls but very risky on long-tail underdogs (Kelly raw can ask for 30%+ of bankroll on extreme value).","book_filtering":"Optimizer endpoints (arbitrage, plus-ev, hedge) and /picks/live all accept a `books` CSV param. Call /api/v1/bookmakers once to discover the valid book keys for your region, then pin a comma-separated list to every subsequent request to scope results to the sportsbooks you actually have accounts at.","arbitrage_execution":"An arbitrage opportunity only holds if both legs fill at the listed prices. Boxing arbs typically persist for hours (vs seconds in NBA/NFL), but partial fills do happen. The optimizer/arbitrage `recommended_first_leg` field tells you which leg to place first — always the softer book, since soft books reprice slower and a sharp-side fill that triggers a soft-side move leaves you stranded."},"groups":[{"key":"discovery","label":"Discovery & health","description":"Self-describing endpoints with no auth. Use to detect freshness, model version, and the production decision policy before pulling picks.","endpoints":[{"path":"/api/v1/health","method":"GET","auth":"public","summary":"Pipeline health & freshness","description":"Cron freshness (age of last successful predict-live run), the active production model version, and current counts of pending vs settled live picks. Returns `status: \"healthy\"` only when the cron has succeeded in the last 30 min (6 missed ticks at the 5-min cadence) AND a model version is loaded.","params":[],"notes":[],"portal_page":null,"example":{"status":"healthy","cron":{"last_ok_started_at":"2026-05-22T06:34:56Z","age_minutes":3},"model":{"version":"lgbm_ens_v5_2026-05-22","fit_at":"2026-05-22T05:00:00Z"},"counts":{"pending_live_picks":22,"settled_live_picks":1431,"predictions_last_24h":188}},"try_url":"https://spartanboxing.ai/api/v1/health"},{"path":"/api/v1/policy","method":"GET","auth":"public","summary":"Production decision policy","description":"The exact gates that turn a prediction into a BET: minimum vig-removed edge, maximum ensemble σ, recommended Kelly fraction and per-bet cap. Re-implement these client-side to replicate the BET/NO BET decision without round-tripping to `/picks/live`.","params":[],"notes":[],"portal_page":null,"example":{"thresholds":{"min_edge_pp":0.03,"max_uncertainty_std":null,"kelly_fraction_default":0.25,"max_bet_pct_default":0.03},"model_version_prefix":"lgbm_ens_v5","audit_report":"master.audit_report kind='sizing_strategy' (boxing-poe): the optional confidence gate trades volume for per-bet quality; default bets every pick clearing the 3% edge floor. Per-setting Sharpe/return/drawdown recompute live in the backtest simulator."},"try_url":"https://spartanboxing.ai/api/v1/policy"},{"path":"/api/v1/docs","method":"GET","auth":"public","summary":"JSON descriptor of this API","description":"Machine-readable catalog of every endpoint, query parameter, and response envelope. Same data as `/portal/api` but in JSON form for tooling.","params":[],"notes":[],"portal_page":null,"example":null,"try_url":"https://spartanboxing.ai/api/v1/docs"}]},{"key":"picks","label":"Picks & decisions","description":"Pending and settled picks with full pricing, Polymarket-aware implied probabilities, and Kelly sizing. The actionable surface for live trading.","endpoints":[{"path":"/api/v1/picks/live","method":"GET","auth":"bearer","summary":"Pending live picks with sizing","description":"Every fight with a pending prediction. Each pick includes the model's fair probability for A win, B win, AND draw (3-class A/B/Draw output from lgbm_ens_v5), the best book + price, raw implied probability, vig-removed implied probability, edge in percentage points, ensemble σ, BET/NO BET verdict with reasons, and (when `bankroll` is provided) a Kelly stake recommendation. Set `include_books=true` to receive the full per-book pricing table plus per-book recomputed stakes (each at that book's price, respecting the per-book min-edge gate).","params":[{"name":"bankroll","in":"query","required":false,"type":"number","default":0,"description":"USD bankroll for stake math. 0 disables sizing."},{"name":"kelly_fraction","in":"query","required":false,"type":"number","default":0.25,"description":"Multiplier on raw Kelly f*. 0.25 = Quarter Kelly."},{"name":"kelly_z","in":"query","required":false,"type":"number","default":0,"description":"σ shrink: p_used = max(0.001, fair − z·σ). 0 = naive Kelly (audit-recommended)."},{"name":"max_bet_pct","in":"query","required":false,"type":"number","default":0.03,"description":"Per-bet stake cap as fraction of bankroll. 0 disables."},{"name":"min_edge_pp","in":"query","required":false,"type":"number","default":0.03,"description":"Minimum vig-removed edge to qualify as BET."},{"name":"max_uncertainty","in":"query","required":false,"type":"number","default":"none (no gate)","description":"Maximum ensemble σ to qualify as BET. Default is no gate. Pass 0.010 to apply the tight filter."},{"name":"bet_only","in":"query","required":false,"type":"boolean","default":false,"description":"Drop NO BET rows from response."},{"name":"include_stale","in":"query","required":false,"type":"boolean","default":false,"description":"Include picks the cron stopped refreshing (likely dropped from OddsAPI)."},{"name":"include_books","in":"query","required":false,"type":"boolean","default":false,"description":"Include full per-book pricing table per pick."},{"name":"books","in":"query","required":false,"type":"string","description":"CSV of sportsbook keys (e.g. `pinnacle,draftkings,fanduel`) to restrict the per-book `books[]` array to. The BET/NO BET decision is UNCHANGED — production policy was audited against global execution. To filter BET eligibility against only YOUR books, use `/api/v1/optimizer/plus-ev` instead."}],"notes":["Stale = predicted before (last_cron_started − 20 min grace). At the 5-min cadence this is 4 missed ticks of slack.","Picks are sorted: BET-eligible first (by stake desc), then NO BET (by edge desc), then stale.","Use `/api/v1/bookmakers` to discover valid book keys for the `books` param."],"portal_page":"/portal/live","example":null,"try_url":"https://spartanboxing.ai/api/v1/picks/live"},{"path":"/api/v1/picks/settled","method":"GET","auth":"bearer","summary":"Settled live picks (P&L reconciliation)","description":"Live picks where the fight has finished and the result is graded. Includes the best price the model recommended vs the closing price for CLV computation, and a `hit` boolean. Use for tracking realized P&L and validating CLV over time.","params":[{"name":"since","in":"query","required":false,"type":"string","default":"7 days ago","description":"ISO date — only picks predicted on or after this date."},{"name":"limit","in":"query","required":false,"type":"integer","default":100,"description":"Max rows. Hard cap 500."}],"notes":[],"portal_page":"/portal/proof-of-edge/picks","example":null,"try_url":"https://spartanboxing.ai/api/v1/picks/settled"},{"path":"/api/v1/picks/{fight_id}","method":"GET","auth":"bearer","summary":"Single pending pick with per-book stakes","description":"Detailed view of one pending pick: model breakdown, every quoted book on the picked side, and per-book Kelly stake suggestions. Returns 404 if there's no pending prediction for `fight_id`.","params":[{"name":"fight_id","in":"path","required":true,"type":"integer","description":"Internal fight identifier (see /api/v1/picks/live or /api/v1/schemas/master/fight)."},{"name":"bankroll","in":"query","required":false,"type":"number","default":0,"description":"Same as /picks/live."},{"name":"kelly_fraction","in":"query","required":false,"type":"number","default":0.25,"description":"Same as /picks/live."},{"name":"kelly_z","in":"query","required":false,"type":"number","default":0,"description":"Same as /picks/live."},{"name":"max_bet_pct","in":"query","required":false,"type":"number","default":0.03,"description":"Same as /picks/live."},{"name":"min_edge_pp","in":"query","required":false,"type":"number","default":0.03,"description":"Same as /picks/live."},{"name":"max_uncertainty","in":"query","required":false,"type":"number","default":"none","description":"Same as /picks/live."},{"name":"books","in":"query","required":false,"type":"string","description":"CSV of sportsbook keys to restrict the per-book `books[]` array to. Global best_market / median_market / worst_market are unchanged."}],"notes":[],"portal_page":"/portal/live","example":null,"try_url":null},{"path":"/api/v1/picks/historical","method":"GET","auth":"bearer","summary":"Walk-forward historical picks","description":"Every walk-forward / holdout / live pick the requested model has emitted on graded fights, with best price, edge, CLV, and hit/miss. Powers the backtest simulator. Filter by `model` (default lgbm_ens), `kind` (dev | holdout | live | all), and `limit`.","params":[{"name":"model","in":"query","required":false,"type":"string","default":"lgbm_ens","description":"One of: lgbm_ens (production), lgbm, pca, baseline."},{"name":"kind","in":"query","required":false,"type":"string","default":"all","description":"Filter by prediction_kind. One of: dev, holdout, live, all."},{"name":"limit","in":"query","required":false,"type":"integer","default":1000,"description":"Max rows returned. Hard cap 10000."}],"notes":["Only fights with `result IN ('a_win', 'b_win')` AND `date <= CURRENT_DATE` are returned — bogus future-dated wiki imports are filtered."],"portal_page":"/portal/proof-of-edge/picks","example":null,"try_url":"https://spartanboxing.ai/api/v1/picks/historical"},{"path":"/api/v1/picks/{fight_id}/explain","method":"GET","auth":"bearer","summary":"Per-feature contribution decomposition","description":"Linearized contribution of each input feature (SHAP-style for LightGBM ensemble models) to the model's fair probability for this fight. Use to sanity-check why the model favored one side.","params":[{"name":"fight_id","in":"path","required":true,"type":"integer","description":"Fight identifier."}],"notes":[],"portal_page":"/portal/proof-of-edge/picks/{fight_id}","example":null,"try_url":null}]},{"key":"optimizer","label":"Bet Optimizer","description":"Cross-book arbitrage scanning, +EV ranking by ROI per dollar, and equal-profit hedge calculation. Every endpoint respects an optional `books` CSV so callers see only opportunities executable at the sportsbooks they actually have accounts at.","endpoints":[{"path":"/api/v1/optimizer/arbitrage","method":"GET","auth":"bearer","summary":"Cross-book arbitrage scanner","description":"For every pending pick, finds the best decimal price on each side across the user's book set. When `1/best_a + 1/best_b < 1`, the spread is an arbitrage that returns the same net on each outcome — a positive result only if both legs fill at the quoted prices (execution and counterparty risk apply; figures are modeled, not assured). Returns one row per opportunity with both legs, the inverse sum, the modeled profit %, and (when `outlay > 0`) a stake split that targets the same gross on each outcome. `recommended_first_leg` flags the leg to place first to reduce partial-fill risk: softer book first since softs reprice slower than sharps.","params":[{"name":"books","in":"query","required":false,"type":"string","description":"CSV of sportsbook keys (e.g. `pinnacle,draftkings,fanduel`) to consider. Omit = all books. An arb at three books you don't have accounts at isn't an opportunity — always set this."},{"name":"min_profit_pct","in":"query","required":false,"type":"number","default":0,"description":"Drop opportunities with a modeled profit below this %. Useful to filter out paper-thin spreads whose margin may not cover bet limits or execution slippage."},{"name":"outlay","in":"query","required":false,"type":"number","description":"Total $ outlay to size the stake split against. When set, each opportunity includes a `recommended_split` with leg_picked / leg_opposite stakes and the equal modeled payout (if both legs fill)."}],"notes":["Boxing arbs typically persist for HOURS — sometimes days — when a soft book drifts off consensus. NBA/NFL arbs vanish in seconds.","Same-book arbs are filtered out (the math holds but they're not actionable cross-book opportunities).","Only pending fights with `date >= today UTC` and a model prediction are scanned. Use /api/v1/picks/live first if you need to verify the underlying pick."],"portal_page":"/portal/live","example":{"fight_id":12345,"fight_date":"2026-05-31","fighter_a":"Karen Chukhadzhian","fighter_b":"Paddy Donovan","leg_picked":{"side":"a","fighter":"Karen Chukhadzhian","book":"betfair_ex_eu","book_classification":"sharp","decimal_odds":2.18,"american_odds":118,"implied_prob_raw":0.4587},"leg_opposite":{"side":"b","fighter":"Paddy Donovan","book":"betmgm","book_classification":"soft","decimal_odds":1.95,"american_odds":-105,"implied_prob_raw":0.5128},"inverse_sum":0.9715,"profit_pct":2.85,"recommended_first_leg":"leg_opposite","recommended_split":{"leg_picked_stake_dollars":235.78,"leg_opposite_stake_dollars":263.42,"total_outlay_dollars":499.2,"locked_payout_dollars":513.42,"locked_profit_dollars":14.22}},"try_url":"https://spartanboxing.ai/api/v1/optimizer/arbitrage"},{"path":"/api/v1/optimizer/plus-ev","method":"GET","auth":"bearer","summary":"+EV opportunities ranked by ROI per dollar","description":"Every model-positive opportunity sorted by `edge / implied_vigfree` — the dollar return per unit of risk capital, not raw edge in percentage points. A 5% edge at decimal odds 1.33 (-300) is a worse bet than a 5% edge at decimal odds 3.00 (+200) because total dollar return per unit risk differs. Compared to `/picks/live`, this endpoint relaxes the strict BET gate so opportunities the production policy rejects (e.g. high σ, low absolute edge) still surface for the user to size with their own discretion.","params":[{"name":"books","in":"query","required":false,"type":"string","description":"CSV of sportsbook keys to consider. Omit = all books."},{"name":"min_edge_pp","in":"query","required":false,"type":"number","default":0,"description":"Minimum vig-removed edge (in fraction, e.g. 0.02 = 2%) to include."},{"name":"max_uncertainty","in":"query","required":false,"type":"number","default":1,"description":"Maximum ensemble σ to include. Default 1.0 = no gate; pass 0.010 to apply the production gate."},{"name":"bankroll","in":"query","required":false,"type":"number","default":0,"description":"USD bankroll for Kelly stake sizing. 0 disables sizing."},{"name":"kelly_fraction","in":"query","required":false,"type":"number","default":0.25,"description":"Kelly multiplier (audit recommends 0.25 = Quarter Kelly)."},{"name":"kelly_z","in":"query","required":false,"type":"number","default":0,"description":"σ shrink: p_used = max(0.001, fair − z·σ). 0 = naive Kelly (production default)."},{"name":"max_bet_pct","in":"query","required":false,"type":"number","default":0.03,"description":"Per-bet stake cap as fraction of bankroll. 0 disables cap."}],"notes":["`roi_per_dollar` is the correct sort key for capital efficiency, not raw `edge_pp`.","Books that quote only one side fall back to raw implied (best.impliedRaw) instead of vig-removed — conservative; understates edge slightly."],"portal_page":"/portal/live","example":null,"try_url":"https://spartanboxing.ai/api/v1/optimizer/plus-ev"},{"path":"/api/v1/optimizer/hedge","method":"GET","auth":"bearer","summary":"Equal-profit hedge calculator","description":"Given a bet you've already placed (`stake` on `side` of `fight_id` at decimal `odds`), returns the stake H on the opposite side that evens out the net P/L across outcomes if the hedge fills. Math: `H = stake × odds / opposite_odds`. Opposite-side price is selected as the best across the user's `books` filter (or all books if omitted). A negative `locked_profit` means the hedge nets a loss — still useful for capping downside on a now-stale bet. All figures are modeled and depend on execution; they are not assured.","params":[{"name":"fight_id","in":"query","required":true,"type":"integer","description":"Internal fight identifier (see /api/v1/picks/live)."},{"name":"stake","in":"query","required":true,"type":"number","description":"USD amount you've already wagered (must be > 0)."},{"name":"odds","in":"query","required":true,"type":"number","description":"Decimal odds your bet is locked in at. Must be > 1. (e.g. 2.10 for +110.)"},{"name":"side","in":"query","required":true,"type":"string","description":"Which side of the fight you bet. Must be `a` or `b`."},{"name":"books","in":"query","required":false,"type":"string","description":"CSV of sportsbook keys to consider for the opposite-side quote. Omit = all books."}],"notes":["Returns 404 `fight_not_found` if the fight has settled or no live pick exists for it.","Returns 404 `no_hedge_quote` if no book in the (possibly filtered) set has a usable opposite-side price."],"portal_page":"/portal/live","example":{"fight_id":12345,"fight_date":"2026-05-31","fighter_a":"Karen Chukhadzhian","fighter_b":"Paddy Donovan","your_bet":{"side":"a","fighter":"Karen Chukhadzhian","stake_dollars":100,"decimal_odds":2.5,"american_odds":150},"hedge":{"side":"b","fighter":"Paddy Donovan","book":"fanduel","decimal_odds":2,"american_odds":100,"stake_dollars":125},"outcome":{"total_outlay_dollars":225,"gross_payout_dollars":250,"locked_profit_dollars":25,"locked_roi":0.1111}},"try_url":"https://spartanboxing.ai/api/v1/optimizer/hedge"}]},{"key":"clv","label":"Closing Line Value","description":"CLV measurements across the universe (devig vs raw close, decay over time, bootstrap CIs). These are the headline edge metrics.","endpoints":[{"path":"/api/v1/clv/evolution","method":"GET","auth":"bearer","summary":"CLV decay over time-to-commence","description":"Median CLV bucketed by how many minutes before fight start the prediction was made. Quantifies alpha decay — bet earlier when CLV is highest. Includes per-fight trajectories and an overall aggregate.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/clv-evolution","example":null,"try_url":"https://spartanboxing.ai/api/v1/clv/evolution"},{"path":"/api/v1/clv/devig","method":"GET","auth":"bearer","summary":"Vig-removed CLV (production headline)","description":"CLV computed against the consensus vig-free closing line rather than the raw best price. Sliced by sharpness universe (sharp books / all books). This is the headline number we report for the model.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/audit","example":null,"try_url":"https://spartanboxing.ai/api/v1/clv/devig"},{"path":"/api/v1/clv/openclose","method":"GET","auth":"bearer","summary":"Open vs close CLV anchored at first quote","description":"CLV computed against the first observed open quote (instead of close). Validates that the model's edge exists at the moment a bet could realistically be placed.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/audit","example":null,"try_url":"https://spartanboxing.ai/api/v1/clv/openclose"},{"path":"/api/v1/clv/ci","method":"GET","auth":"bearer","summary":"Bootstrap confidence intervals on CLV","description":"1000-iter bootstrap of mean CLV with 95% CI, by sharpness universe and period. Use when arguing edge is statistically distinguishable from zero.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/audit","example":null,"try_url":"https://spartanboxing.ai/api/v1/clv/ci"}]},{"key":"model","label":"Model introspection","description":"Internals of the production lgbm_ens_v5 model — feature importances, rolling metrics, calibration, refit P&L, holdout audit, MC Kelly survival curves.","endpoints":[{"path":"/api/v1/model/summary","method":"GET","auth":"bearer","summary":"All trained model versions with headline metrics","description":"One row per model_version (logistic_v1, lgbm_v1, lgbm_ens_v5, …) with n_train, log loss, Brier, ROC AUC, and dev CLV. Use to pick a model for `picks/historical`.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/model","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/summary"},{"path":"/api/v1/model/comparison","method":"GET","auth":"bearer","summary":"Head-to-head metrics across model families","description":"Side-by-side comparison points for plotting model families on the same axis.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/model","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/comparison"},{"path":"/api/v1/model/coefs","method":"GET","auth":"bearer","summary":"Coefficient time series (logistic model)","description":"Per-feature coefficient at each refit cutoff date. For the logistic baselines only — LightGBM coefficient analogs live in `/model/feature-pnl`.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/regression","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/coefs"},{"path":"/api/v1/model/rolling","method":"GET","auth":"bearer","summary":"Rolling log loss / Brier / accuracy","description":"Time-bucketed rolling out-of-sample metrics for the production model, used by the model-quality charts.","params":[{"name":"window","in":"query","required":false,"type":"integer","default":50,"description":"Rolling window size in predictions (1–500)."}],"notes":[],"portal_page":"/portal/proof-of-edge/diag","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/rolling"},{"path":"/api/v1/model/calibration","method":"GET","auth":"bearer","summary":"Calibration bins with bootstrap CI","description":"Reliability bins (predicted probability vs empirical hit rate). Tells you where the model is over/under-confident.","params":[{"name":"bins","in":"query","required":false,"type":"integer","default":10,"description":"Number of equal-width bins on [0, 1] (3–30)."}],"notes":[],"portal_page":"/portal/proof-of-edge/diag","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/calibration"},{"path":"/api/v1/model/refit-sample","method":"GET","auth":"bearer","summary":"In-sample vs out-of-sample refit quality","description":"For each walk-forward refit cutoff, in-sample vs out-of-sample log loss / Brier / CLV.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/model","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/refit-sample"},{"path":"/api/v1/model/feature-pnl","method":"GET","auth":"bearer","summary":"P&L attribution per feature","description":"Permutation-style attribution: how much CLV is captured per feature, by period. Shows which features actually carry the edge.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/feature-pnl","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/feature-pnl"},{"path":"/api/v1/model/regression","method":"GET","auth":"bearer","summary":"Per-feature univariate regression + collinearity","description":"Single-feature regression coefficients, bin-by-bin response, stability over time, collinearity groups, and a final classification (kept / collinear / noisy).","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/regression","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/regression"},{"path":"/api/v1/model/pca","method":"GET","auth":"bearer","summary":"Adaptive-PCA refits (historical)","description":"Component selection per refit for the logistic_v2_pca model family. Historical/reference only — production no longer uses PCA.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/pca","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/pca"},{"path":"/api/v1/model/mc-kelly","method":"GET","auth":"bearer","summary":"Monte Carlo Kelly bankroll-growth simulations","description":"Distribution of terminal bankroll over N simulated bet sequences for each (kelly fraction, universe) combo. Includes recommended fraction.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/mc-kelly","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/mc-kelly"},{"path":"/api/v1/model/holdout-audit","method":"GET","auth":"bearer","summary":"Holdout-audit report (full)","description":"Sealed holdout audit: period-by-period metrics, feature shift checks, book coverage, per-book overround, and seal state. The strictest test we run.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/holdout-audit","example":null,"try_url":"https://spartanboxing.ai/api/v1/model/holdout-audit"}]},{"key":"audit","label":"Data & audit","description":"Underlying data quality, distributions, odds-source coverage, and the resolution log for ingestion provenance.","endpoints":[{"path":"/api/v1/audit/data-quality","method":"GET","auth":"bearer","summary":"Master schema data quality report","description":"Outcome coverage by year and weight class, fighter bio coverage, feature zero-rate, and totals. Used to flag silent data drift before it poisons the model.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/quality","example":null,"try_url":"https://spartanboxing.ai/api/v1/audit/data-quality"},{"path":"/api/v1/audit/corpus","method":"GET","auth":"bearer","summary":"Corpus health (fights, fighters, books)","description":"Totals by date bucket — fights, fighters with bios, books quoting, dev/holdout split.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge","example":null,"try_url":"https://spartanboxing.ai/api/v1/audit/corpus"},{"path":"/api/v1/audit/odds-comparison","method":"GET","auth":"bearer","summary":"Per-book pricing comparison","description":"Pinnacle-vs-soft pricing dispersion, overround stats, and book coverage per fight for the production model.","params":[{"name":"limit","in":"query","required":false,"type":"integer","default":500,"description":"Max comparison rows (10–2000)."}],"notes":[],"portal_page":"/portal/proof-of-edge/odds-comparison","example":null,"try_url":"https://spartanboxing.ai/api/v1/audit/odds-comparison"},{"path":"/api/v1/audit/resolution-log","method":"GET","auth":"bearer","summary":"Fighter-resolution provenance log","description":"Per-fighter resolution attempts (BoxRec, BDA, Wikipedia, manual override). Tracks which source supplied which record and any conflicts.","params":[{"name":"limit","in":"query","required":false,"type":"integer","default":100,"description":"Max rows. Cap 500."},{"name":"search","in":"query","required":false,"type":"string","description":"Case-insensitive fighter-name substring."}],"notes":[],"portal_page":"/portal/proof-of-edge/data-sources","example":null,"try_url":"https://spartanboxing.ai/api/v1/audit/resolution-log"},{"path":"/api/v1/audit/distributions","method":"GET","auth":"bearer","summary":"Feature value distributions","description":"Histograms for every input feature. Used to verify feature ranges are stable across refit cutoffs.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/distributions","example":null,"try_url":"https://spartanboxing.ai/api/v1/audit/distributions"}]},{"key":"bookmakers","label":"Bookmakers","description":"Catalog of every sportsbook the system tracks: region, sharp/soft classification, current pending-pick coverage, and per-book median overround (vig). Powers per-user book selection across the optimizer + picks endpoints.","endpoints":[{"path":"/api/v1/bookmakers","method":"GET","auth":"bearer","summary":"Catalog of all known sportsbooks","description":"Every sportsbook the system tracks, with region (EU / UK / US / US₂ / AU / other), sharp-vs-soft classification, current pending-pick coverage, and median per-book overround (book's vig in plain percentage points). Built dynamically from the union of the static region map and any book currently quoting at least one pending pick. Use to populate a per-user book picker, dynamically filter Bet Optimizer scans, or sanity-check `books=` CSVs against books with active quotes.","params":[],"notes":["Sharp = Pinnacle, Bookmaker.eu, BetMGM, Circa, etc. Source list is `SHARP_BOOKS` in `src/lib/proof-of-edge.ts`.","`median_overround_pp` of 0.05 = 5% vig (typical soft book). Pinnacle / sharp books typically run 2-4%."],"portal_page":"/portal/live","example":[{"book":"pinnacle","region":"eu","classification":"sharp","pending_picks_quoting":22,"pending_picks_paired":22,"median_overround_pp":0.0264},{"book":"draftkings","region":"us","classification":"soft","pending_picks_quoting":18,"pending_picks_paired":18,"median_overround_pp":0.0571}],"try_url":"https://spartanboxing.ai/api/v1/bookmakers"}]},{"key":"fighters","label":"Fighters","description":"Per-fighter biographical and feature data backing the prediction model. List + detail endpoints.","endpoints":[{"path":"/api/v1/fighters","method":"GET","auth":"bearer","summary":"Paginated, searchable fighter list","description":"All fighters in `master.fighter` with biographical fields and total-fight count. Stable sort on `fighter_id ASC` for paginated iteration; `meta.pagination.next_offset` tells you the next page offset (or `null` when at end). For deeper iteration than 100K rows, narrow via `search`.","params":[{"name":"search","in":"query","required":false,"type":"string","description":"Case-insensitive name substring (ILIKE %search%). Trimmed."},{"name":"limit","in":"query","required":false,"type":"integer","default":100,"description":"Max rows per page. Hard cap 500."},{"name":"offset","in":"query","required":false,"type":"integer","default":0,"description":"Skip count for pagination. Hard cap 100000."},{"name":"has_bio","in":"query","required":false,"type":"boolean","default":false,"description":"When true, drop rows with no biometrics populated (no dob AND no stance AND no height AND no reach)."}],"notes":[],"portal_page":"/portal/fighters","example":[{"fighter_id":1248,"name":"Karen Chukhadzhian","source":"boxrec","dob":"1996-05-23","stance":"orthodox","height_cm":178,"reach_cm":183,"nationality":"Ukraine","total_fights":28}],"try_url":"https://spartanboxing.ai/api/v1/fighters"},{"path":"/api/v1/fighters/{fighter_id}","method":"GET","auth":"bearer","summary":"Fighter detail (bio + record + resolution log)","description":"Bio fields, current Glicko rating, full record, manual-override status, and the 50 most recent ingestion resolution attempts.","params":[{"name":"fighter_id","in":"path","required":true,"type":"integer","description":"Internal fighter identifier (see /api/v1/fighters or /api/v1/schemas/master/fighter)."}],"notes":[],"portal_page":"/portal/proof-of-edge/fighters/{fighter_id}","example":null,"try_url":null}]},{"key":"journal","label":"Bet Journal","description":"Per-user bet journal — log, list, settle, and analyze your bets with point-in-time model snapshots. Full CRUD + analytics aggregates.","endpoints":[{"path":"/api/v1/journal/bets","method":"GET","auth":"bearer","summary":"List your logged bets","description":"Returns all bets in your journal ordered by placement date (newest first). Each bet includes the point-in-time model snapshot captured at log time, settlement status, P/L, and realized CLV where available.","params":[],"notes":[],"portal_page":"/portal/bet-journal","example":{"id":"uuid","source":"pick","fightId":1234,"pickedFighter":"Oleksandr Usyk","bookKey":"pinnacle","decimalOdds":1.85,"americanOdds":-118,"stake":250,"currency":"USD","snapshotEdge":0.042,"snapshotClvAtLog":0.038,"status":"won","payout":462.5,"realizedClv":0.035},"try_url":"https://spartanboxing.ai/api/v1/journal/bets"}]},{"key":"lines","label":"Line History","description":"Universal line movement data for any fight. Per-book time series, market median, and summary stats. Public endpoints — no auth required.","endpoints":[]},{"key":"my-bookmakers","label":"My Bookmakers","description":"Per-user sportsbook account management — save bookmaker credentials, VPN locations, and balances.","endpoints":[]},{"key":"admin","label":"Admin (all users)","description":"System-wide endpoints that cross the per-user boundary: list ALL users, ALL bets, ALL bookmakers and notification prefs, plus aggregate dashboard stats. These require the system API_TOKEN (not a per-user sbp_ key) and back an admin app/dashboard frontend with full read access to every user's data.","endpoints":[{"path":"/api/v1/admin/stats","method":"GET","auth":"bearer","summary":"System-wide dashboard stats","description":"Aggregate counts for an admin dashboard: total/new users, bet totals (pending, settled, total stake, realized P&L, mean CLV), pipeline health (last cron run age, 24h run count, healthy flag), and v3 live/expired prediction counts. Requires the system API_TOKEN.","params":[],"notes":[],"portal_page":null,"example":{"users":{"total":42,"last_7d":5,"last_30d":18},"bets":{"total":310,"pending":64,"settled":246,"total_stake":26350,"realized_pnl":1240.5,"mean_clv":0.031},"pipeline":{"last_run_age_minutes":3,"last_run_ok":true,"runs_24h":287,"healthy":true},"predictions":{"live":48,"expired":213}},"try_url":"https://spartanboxing.ai/api/v1/admin/stats"},{"path":"/api/v1/admin/users","method":"GET","auth":"bearer","summary":"List ALL users with stats","description":"Every registered user with aggregate stats: bet count, pending/settled split, total stake, realized P&L, mean CLV, saved-bookmaker count, active API-key count, and notification status. Requires the system API_TOKEN.","params":[{"name":"limit","in":"query","required":false,"type":"integer","default":500,"description":"Max rows. Hard cap 5000."},{"name":"offset","in":"query","required":false,"type":"integer","default":0,"description":"Pagination offset."},{"name":"search","in":"query","required":false,"type":"string","description":"Case-insensitive email/name substring filter."}],"notes":[],"portal_page":null,"example":{"id":"uuid","email":"user@example.com","name":"Jane Doe","createdAt":"2026-05-01T12:00:00Z","betCount":23,"pendingBets":4,"settledBets":19,"totalStake":1955,"realizedPnl":120.5,"meanClv":0.028,"bookmakerCount":3,"apiKeyCount":1,"notificationsEnabled":true},"try_url":"https://spartanboxing.ai/api/v1/admin/users"},{"path":"/api/v1/admin/users/{userId}","method":"GET","auth":"bearer","summary":"Full profile for one user","description":"Complete record for a single user: account info, EVERY logged bet (with point-in-time model snapshot columns), notification preferences, saved bookmakers, and API-key metadata (prefixes only — never the raw token). Requires the system API_TOKEN.","params":[{"name":"userId","in":"path","required":true,"type":"string","description":"User UUID (from /api/v1/admin/users)."}],"notes":[],"portal_page":null,"example":null,"try_url":null},{"path":"/api/v1/admin/bets","method":"GET","auth":"bearer","summary":"ALL bets across all users","description":"Every bet in the system with the owning user's id + email joined. Paginated and filterable by status, user, or date. Meta includes total count, total stake, and mean CLV for the filtered set. Requires the system API_TOKEN.","params":[{"name":"limit","in":"query","required":false,"type":"integer","default":500,"description":"Max rows. Hard cap 5000."},{"name":"offset","in":"query","required":false,"type":"integer","default":0,"description":"Pagination offset."},{"name":"status","in":"query","required":false,"type":"string","description":"Filter by status: pending | won | lost | push."},{"name":"user_id","in":"query","required":false,"type":"string","description":"Filter to a single user UUID."},{"name":"since","in":"query","required":false,"type":"string","description":"ISO date — only bets placed on/after this date."}],"notes":[],"portal_page":null,"example":null,"try_url":"https://spartanboxing.ai/api/v1/admin/bets"},{"path":"/api/v1/admin/bookmakers","method":"GET","auth":"bearer","summary":"ALL saved bookmakers","description":"Every saved bookmaker account across all users (book key, username, account id, VPN location, balance, currency, notes, active flag) with the owning user's email. Requires the system API_TOKEN.","params":[{"name":"user_id","in":"query","required":false,"type":"string","description":"Filter to a single user UUID."}],"notes":[],"portal_page":null,"example":null,"try_url":"https://spartanboxing.ai/api/v1/admin/bookmakers"},{"path":"/api/v1/admin/notifications","method":"GET","auth":"bearer","summary":"ALL notification preferences","description":"Every user's notification preferences (enabled flag, min edge, max uncertainty, min stake, bankroll) with the owning user's email. Requires the system API_TOKEN.","params":[],"notes":[],"portal_page":null,"example":null,"try_url":"https://spartanboxing.ai/api/v1/admin/notifications"}]},{"key":"ops","label":"Operations","description":"Live cron run log and the latest persisted walk-forward backtest snapshot.","endpoints":[{"path":"/api/v1/runs","method":"GET","auth":"bearer","summary":"Live cron run log","description":"Recent predict-live ticks with start time, duration, OddsAPI credits consumed, ok/failure status.","params":[{"name":"limit","in":"query","required":false,"type":"integer","default":25,"description":"Max rows. Cap 200."}],"notes":[],"portal_page":"/portal/proof-of-edge/run","example":null,"try_url":"https://spartanboxing.ai/api/v1/runs"},{"path":"/api/v1/backtest/latest","method":"GET","auth":"bearer","summary":"Latest persisted walk-forward backtest","description":"Most recent backtest summary row: total picks, hit rate, devig CLV, Sharpe, refits, runtime.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/backtest","example":null,"try_url":"https://spartanboxing.ai/api/v1/backtest/latest"}]},{"key":"schema","label":"Schema browser","description":"Generic read-only access to the master schema. Tables, columns, and sampled rows.","endpoints":[{"path":"/api/v1/schemas","method":"GET","auth":"bearer","summary":"List all schemas + tables + row counts (system token only)","description":"Discovery endpoint for the database surface. Returns schemas (`master`, `bronze`, `public`, …) and their tables. ADMIN-ONLY: requires the system API token — per-user keys receive 403.","params":[],"notes":[],"portal_page":"/portal/proof-of-edge/schema","example":null,"try_url":"https://spartanboxing.ai/api/v1/schemas"},{"path":"/api/v1/schemas/{schema}/{table}","method":"GET","auth":"bearer","summary":"Columns + sample rows for one table (system token only)","description":"Column types, nullability, and up to 100 sample rows. Read-only. ADMIN-ONLY: requires the system API token — per-user keys receive 403. Credential tables are never browsable.","params":[{"name":"schema","in":"path","required":true,"type":"string","description":"Schema name (e.g. master)."},{"name":"table","in":"path","required":true,"type":"string","description":"Table name."},{"name":"limit","in":"query","required":false,"type":"integer","default":50,"description":"Max sample rows. Cap 100."}],"notes":[],"portal_page":"/portal/proof-of-edge/schema/{schema}/{table}","example":null,"try_url":null}]}],"counts":{"total_endpoints":45,"public":3,"bearer":42,"groups":14}},"meta":{"generated_at":"2026-07-07T20:31:31.053Z","api_version":"v1"}}