{"openapi":"3.1.0","info":{"title":"FLOPS Intelligence Layer — Demo","description":"Local-only demo of the v0.5 intelligence layer endpoints. Uses in-memory data; no auth; no rate limiting. For the production stack see src/delivery/app.py.","version":"v0.5-demo"},"paths":{"/healthz":{"get":{"summary":"Healthz","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Healthz Healthz Get"}}}}}}},"/readyz":{"get":{"summary":"Readyz","operationId":"readyz_readyz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Readyz Readyz Get"}}}}}}},"/v1/analytics/tco/compute":{"post":{"tags":["analytics"],"summary":"Tco Compute","description":"Decompose $/GPU-hr into acquisition / power / cooling / ops.\n\nPure math — no Redis dependency. Fast path: P50 < 50ms.","operationId":"tco_compute_v1_analytics_tco_compute_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TCOComputeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TCOComputeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/tco/sensitivity":{"post":{"tags":["analytics"],"summary":"Tco Sensitivity","description":"Sensitivity tornado — one-variable-at-a-time TCO impact analysis.\n\nFor each variable in the ``_SENSITIVITY_SPEC`` list, compute the TCO at\n``(baseline − delta)`` and ``(baseline + delta)``. Returns variables\nsorted descending by the absolute range of impact.\n\nMethodology: see ``docs/methodology/TCO_SENSITIVITY_METHODOLOGY.md``.\nEvery perturbation range carries an inline ``source`` citation.","operationId":"tco_sensitivity_v1_analytics_tco_sensitivity_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TCOComputeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensitivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/procurement/buy-vs-lease":{"post":{"tags":["analytics"],"summary":"Buy Vs Lease","operationId":"buy_vs_lease_v1_analytics_procurement_buy_vs_lease_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuyVsLeaseRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BuyVsLeaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/procurement/breakeven":{"post":{"tags":["analytics"],"summary":"Breakeven","operationId":"breakeven_v1_analytics_procurement_breakeven_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreakevenRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BreakevenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/analytics/procurement/timing/{sku}":{"get":{"tags":["analytics"],"summary":"Timing Read","operationId":"timing_read_v1_analytics_procurement_timing__sku__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimingReadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reports/benchmarking/{operator_hash}":{"get":{"tags":["reports"],"summary":"Benchmarking","description":"Peer benchmarking report for an operator.\n\nResponse shape depends on whether cohort clears k-anonymity:\n- Pass: NoisyCohortPosition with DP confidence intervals on percentiles.\n- Fail: SubThresholdFallback with absolute_dollar_delta only.\n\nAlways includes ``absolute_dollar_delta`` at the top level (UAT-05(d) —\nlead value, does not require k-anon).","operationId":"benchmarking_v1_reports_benchmarking__operator_hash__get","parameters":[{"name":"operator_hash","in":"path","required":true,"schema":{"type":"string","title":"Operator Hash"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","title":"Period"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Benchmarking V1 Reports Benchmarking  Operator Hash  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/reports/benchmarking/{operator_hash}/sku-mix":{"get":{"tags":["reports"],"summary":"Benchmarking Sku Mix","description":"Peer-group SKU mix for an operator's segment — Task #13.\n\nReturns the GPU SKU distribution across operators in the same\n``(segment, workload, phase, region)`` peer group. Procurement-buyer\npersona read: \"what mix do my peers run?\". Privacy bar matches\n``/benchmarking`` (k-anon + Laplace DP via ``cohort_sku_mix``).\n\nResponse:\n    ``{ peer_group_size, k_threshold, suppressed: bool,\n        breakdown: [{sku, pct, member_count}, ...],\n        segment, workload, phase, region, week, methodology }``\n\nWhen ``suppressed=True`` the peer group failed k-anon; ``breakdown``\nis empty and the frontend should render the \"still gathering\" empty\nstate. The methodology string is included verbatim for transparency.","operationId":"benchmarking_sku_mix_v1_reports_benchmarking__operator_hash__sku_mix_get","parameters":[{"name":"operator_hash","in":"path","required":true,"schema":{"type":"string","title":"Operator Hash"}},{"name":"period","in":"query","required":true,"schema":{"type":"string","title":"Period"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Benchmarking Sku Mix V1 Reports Benchmarking  Operator Hash  Sku Mix Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/registry":{"get":{"tags":["site-intelligence"],"summary":"Get Registry","operationId":"get_registry_v1_site_intelligence_registry_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by status","title":"Status"},"description":"Filter by status"},{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by category","title":"Category"},"description":"Filter by category"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Registry V1 Site Intelligence Registry Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/summary":{"get":{"tags":["site-intelligence"],"summary":"Get Summary","operationId":"get_summary_v1_site_intelligence_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Summary V1 Site Intelligence Summary Get"}}}}}}},"/v1/site-intelligence/source/{source_id}":{"get":{"tags":["site-intelligence"],"summary":"Get Source Detail","operationId":"get_source_detail_v1_site_intelligence_source__source_id__get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Source Detail V1 Site Intelligence Source  Source Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/region/{region}/fiber":{"get":{"tags":["site-intelligence"],"summary":"Get Region Fiber","description":"Per-region fiber resilience surface.\n\nReads the latest ``telegeography_fiber`` record for ``region`` and\nexposes the raw payload fields the F11 fiber overlay + the lender\nmethodology section both depend on. Plugs the observability gap\nthat the R5 cable enum v3 expansion (67 → 166 cables) created:\noperator_count_200km + cable_count_200km only flowed into the\nrecord payload, with no caller-facing endpoint to read them.\n\nRegion naming follows the telegeography_fiber collector's\ncentroid map (us_east, us_west, us_central, us_midwest, canada,\neu_west, uk, nordics, apac_east, apac_south, oceania,\nsouth_america). On a cold cache the seed payload is returned\nwith ``record_type='fiber_landing_density_seed'`` and a slightly\ndifferent field set (``operator_count_seed`` instead of\n``operator_count_200km``); the response merges both shapes into\na stable envelope so callers don't need to branch on cache state.\n\nAuth mirrors /score — no route-level Depends; the dashboard\nbasic-auth at app mount enforces access. Tier gating intentionally\nomitted because the data is non-sensitive (it's TeleGeography 2025\npublic + harvested cable→operator mapping).","operationId":"get_region_fiber_v1_site_intelligence_region__region__fiber_get","parameters":[{"name":"region","in":"path","required":true,"schema":{"type":"string","title":"Region"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Region Fiber V1 Site Intelligence Region  Region  Fiber Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/fiber-landings.geojson":{"get":{"tags":["site-intelligence"],"summary":"Fiber Landings Geojson","description":"Submarine cable landings as a raw GeoJSON FeatureCollection.\n\nBacks the F11 FIBER map-layer toggle. Reads the cached payload that\nTeleGeographySubmarineLandingsCollector writes (~1,900 worldwide\nlandings, refreshed every 30 days). Returns an empty FeatureCollection\nwhen the cache is cold so the map layer just renders blank rather\nthan 5xx-ing — the cache-warmer cron will populate on next run.\n\nCache-friendly: methodology-stable data with a 30-day refresh\ncadence; Cloudflare can cache aggressively in front of this.","operationId":"fiber_landings_geojson_v1_site_intelligence_fiber_landings_geojson_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Fiber Landings Geojson V1 Site Intelligence Fiber Landings Geojson Get"}}}}}}},"/v1/site-intelligence/run":{"post":{"tags":["site-intelligence"],"summary":"Run Collectors","operationId":"run_collectors_v1_site_intelligence_run_post","parameters":[{"name":"force","in":"query","required":false,"schema":{"type":"boolean","description":"Bypass TTL cache (requires X-Force-Token in prod)","default":false,"title":"Force"},"description":"Bypass TTL cache (requires X-Force-Token in prod)"},{"name":"parallel","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Parallel"}},{"name":"categories","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Limit to these categories","title":"Categories"},"description":"Limit to these categories"},{"name":"max_workers","in":"query","required":false,"schema":{"type":"integer","maximum":8,"minimum":1,"description":"Concurrent collectors (memory-bounded)","default":3,"title":"Max Workers"},"description":"Concurrent collectors (memory-bounded)"},{"name":"X-Force-Token","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Force-Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Run Collectors V1 Site Intelligence Run Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/coverage":{"get":{"tags":["site-intelligence"],"summary":"Site Intel Coverage","description":"Per-source jurisdiction coverage in the recent window.\n\nSurfaces the platform-breadth signal: which collectors confirmed\nreachable upstream data in the last window, and across how many\njurisdictions. Different from the registry view (which says \"this\ncollector exists\") — this says \"this collector actually pulled\ndata for these specific jurisdictions recently.\"\n\nReturns 503 if persistence isn't reachable (DATABASE_URL unset\nin dev/local).","operationId":"site_intel_coverage_v1_site_intelligence_coverage_get","parameters":[{"name":"window_hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"How far back to look for coverage (1-720 hours)","default":24,"title":"Window Hours"},"description":"How far back to look for coverage (1-720 hours)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Site Intel Coverage V1 Site Intelligence Coverage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/carbon-intensity":{"get":{"tags":["site-intelligence"],"summary":"Site Intel Carbon Intensity","description":"Per-region grid carbon intensity (gCO2eq/kWh) derived from the\n``gen_mix`` table — R13 Part B. Feeds the L4 F11 siting carbon\noverlay + the ``get_carbon_intensity(region)`` MCP tool.\n\nEU regions populate today from the ENTSO-E A75 collector; US regions\npopulate once the operator-gated EIA Form-930 backfill runs\n(EIA_API_KEY — see docs/ops/r13-verification-log.md). Each region\ncarries ``known_fraction`` so a consumer can downweight a mix that's\nmostly unmapped 'other' fuel.\n\nReturns 503 if persistence isn't reachable.","operationId":"site_intel_carbon_intensity_v1_site_intelligence_carbon_intensity_get","parameters":[{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to one canonical region (e.g. eu-west)","title":"Region"},"description":"Filter to one canonical region (e.g. eu-west)"},{"name":"lookback_hours","in":"query","required":false,"schema":{"type":"integer","maximum":8760,"minimum":1,"description":"Trailing window over which to average the mix","default":168,"title":"Lookback Hours"},"description":"Trailing window over which to average the mix"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Site Intel Carbon Intensity V1 Site Intelligence Carbon Intensity Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/freshness":{"get":{"tags":["site-intelligence"],"summary":"Site Intel Freshness","description":"Per-implemented-source freshness audit. Universal silent-failure detector.\n\nFor every ``status='implemented'`` source in SOURCE_REGISTRY, computes:\n  - latest_seen (None if never wrote a record)\n  - hours_since_last_record\n  - status (ok|warn|critical|never_seen|skipped) based on its\n    registry cadence vs the threshold table above.\n\nA status of ``critical`` or ``never_seen`` means the collector has\nsilently failed beyond plausibly explainable variance. Wire to\nSentry / PagerDuty / a dashboard banner — anything that gets a\nhuman's attention faster than the next demo.\n\nWhen ``per_jurisdiction=true`` is set, each source row gains a\n``jurisdictions[]`` array with the per-jurisdiction latest_seen +\nstatus, so an operator can tell WHICH region a multi-jurisdiction\ncollector is dark in (e.g. ISO-NE ok, ERCOT critical). Reads a\n7-day window — matches the longest cadence that meaningfully\nfans out across jurisdictions. Costs one extra GROUP BY query.\n\nReturns 503 if DB unreachable (same convention as /coverage).","operationId":"site_intel_freshness_v1_site_intelligence_freshness_get","parameters":[{"name":"per_jurisdiction","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Per Jurisdiction"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Site Intel Freshness V1 Site Intelligence Freshness Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/last-run":{"get":{"tags":["site-intelligence"],"summary":"Last Run","description":"Most recent finished run summary.\n\nDB path (post-2026-05-10 cutover): reads the latest finished row\nfrom ``site_intel_runs`` so the dashboard staleness banner reflects\nthe durable hourly worker, not whatever the dashboard process\nhappens to have run. The in-process ``_LAST_RUN`` is the fallback\nfor dev/local environments without persistence enabled.","operationId":"last_run_v1_site_intelligence_last_run_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Last Run V1 Site Intelligence Last Run Get"}}}}}}},"/v1/site-intelligence/emissions/top-plants":{"get":{"tags":["site-intelligence"],"summary":"Emissions Top Plants","description":"Top power-plant emitters from the most recent Climate TRACE run.\n\nPrimary source (post-2026-05-10): durable ``site_intel_records``\npopulated hourly by ``flops-site-intel-collector``. Fallback for\ndev/local: the in-process ``_LAST_RUN`` cache. Returns 503 only when\nboth are empty.","operationId":"emissions_top_plants_v1_site_intelligence_emissions_top_plants_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max rows to return; 1-500","default":20,"title":"Limit"},"description":"Max rows to return; 1-500"},{"name":"fuel","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Substring filter on AssetType (e.g. 'coal', 'gas')","title":"Fuel"},"description":"Substring filter on AssetType (e.g. 'coal', 'gas')"},{"name":"min_co2e_t","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0},{"type":"null"}],"description":"Drop plants below this CO2e tonnes/yr","title":"Min Co2E T"},"description":"Drop plants below this CO2e tonnes/yr"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Emissions Top Plants V1 Site Intelligence Emissions Top Plants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/score":{"get":{"tags":["site-intelligence"],"summary":"Get Scores","description":"Per-region factor scores + composite for the site selector.\n\nReturns a list of region scoring envelopes ordered for side-by-side\ncomparison in the dashboard. Each envelope contains the §7 factor\nbreakdown, the §7.12 weighted composite, and an explicit rationale\nper factor so the UI can show *why* a region scored what it did.","operationId":"get_scores_v1_site_intelligence_score_get","parameters":[{"name":"workload","in":"query","required":false,"schema":{"type":"string","description":"Workload class — see /workloads","default":"hyperscale_training","title":"Workload"},"description":"Workload class — see /workloads"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"If set, return only this region's score","title":"Region"},"description":"If set, return only this region's score"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Scores V1 Site Intelligence Score Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/workloads":{"get":{"tags":["site-intelligence"],"summary":"Get Workloads","description":"Workload metadata + region list for the workload selector dropdown.","operationId":"get_workloads_v1_site_intelligence_workloads_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Workloads V1 Site Intelligence Workloads Get"}}}}}}},"/v1/site-intelligence/states":{"get":{"tags":["site-intelligence"],"summary":"Get States","description":"Drill-through scoring below the macro-region tier.\n\nFor US regions (us_east, us_west) → US states.\nFor EU/UK regions (eu_west, uk)    → countries (DE/FR/NL/IE/ES/IT for\n                                     eu_west, GB for uk).\n\nIf ``state`` is provided, returns just that unit's full breakdown.\nThe ``state`` field in each envelope carries the unit code (US state\ncode for states, ISO 3166-1 alpha-2 for countries) so the dashboard\ncan render both tiers through the same UI.","operationId":"get_states_v1_site_intelligence_states_get","parameters":[{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to states/countries in this region","title":"Region"},"description":"Filter to states/countries in this region"},{"name":"workload","in":"query","required":false,"schema":{"type":"string","default":"hyperscale_training","title":"Workload"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Single state/country (overrides region)","title":"State"},"description":"Single state/country (overrides region)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get States V1 Site Intelligence States Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/counties":{"get":{"tags":["site-intelligence"],"summary":"Get Counties","description":"Per-county scoring envelope. Top-30 US data-center counties +\n6 Canadian metros (Toronto, Montréal, Vancouver, Calgary, Québec\nCity, Ottawa). Counties not in the curated list 404.","operationId":"get_counties_v1_site_intelligence_counties_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to counties under this state/province (e.g. VA, ON)","title":"State"},"description":"Filter to counties under this state/province (e.g. VA, ON)"},{"name":"county","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Single county FIPS or metro id (overrides state)","title":"County"},"description":"Single county FIPS or metro id (overrides state)"},{"name":"workload","in":"query","required":false,"schema":{"type":"string","default":"hyperscale_training","title":"Workload"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Counties V1 Site Intelligence Counties Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/transmission/proximity":{"get":{"tags":["site-intelligence"],"summary":"Get Transmission Proximity","description":"Nearest substations / transmission lines / generators around a point.\n\nReturns the AI-DC siting view of the grid: ranked by distance with\nfull feature properties (operator, voltage, fuel mix). Empty lists\nwhen the cache is cold — call POST /run with categories=power_grid_us\nto warm it.","operationId":"get_transmission_proximity_v1_site_intelligence_transmission_proximity_get","parameters":[{"name":"lat","in":"query","required":true,"schema":{"type":"number","description":"Latitude (WGS84)","title":"Lat"},"description":"Latitude (WGS84)"},{"name":"lon","in":"query","required":true,"schema":{"type":"number","description":"Longitude (WGS84)","title":"Lon"},"description":"Longitude (WGS84)"},{"name":"radius_km","in":"query","required":false,"schema":{"type":"number","maximum":200.0,"minimum":0.1,"description":"Search radius (km)","default":25.0,"title":"Radius Km"},"description":"Search radius (km)"},{"name":"min_kv_substation","in":"query","required":false,"schema":{"type":"integer","description":"Minimum substation voltage (kV)","default":100,"title":"Min Kv Substation"},"description":"Minimum substation voltage (kV)"},{"name":"min_kv_line","in":"query","required":false,"schema":{"type":"integer","description":"Minimum line voltage (kV)","default":230,"title":"Min Kv Line"},"description":"Minimum line voltage (kV)"},{"name":"min_mw","in":"query","required":false,"schema":{"type":"number","description":"Minimum generator capacity (MW)","default":1.0,"title":"Min Mw"},"description":"Minimum generator capacity (MW)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Max results per kind","default":20,"title":"Limit"},"description":"Max results per kind"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Transmission Proximity V1 Site Intelligence Transmission Proximity Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/transmission/region":{"get":{"tags":["site-intelligence"],"summary":"Get Transmission Region","description":"Aggregate transmission stats + transmission_score (0-100) for a region.\n\nThe ``transmission_score`` here is what feeds the live-overlay path\nof the F11 transmission factor. Always-honest: returns\n``available: false`` if no cached data covers the region.","operationId":"get_transmission_region_v1_site_intelligence_transmission_region_get","parameters":[{"name":"region","in":"query","required":true,"schema":{"type":"string","description":"FLOPS macro region (us_east, us_west, ...)","title":"Region"},"description":"FLOPS macro region (us_east, us_west, ...)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Transmission Region V1 Site Intelligence Transmission Region Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/site-intelligence/transmission/geojson":{"get":{"tags":["site-intelligence"],"summary":"Get Transmission Geojson","description":"Map-ready FeatureCollection for a region. Used by the F11 map layer.\n\nFilters applied here keep the response under ~1 MB even for the\ndensest US macro region (us_east). Geometry is preserved verbatim\nfrom the cached HIFLD/OSM data.","operationId":"get_transmission_geojson_v1_site_intelligence_transmission_geojson_get","parameters":[{"name":"region","in":"query","required":true,"schema":{"type":"string","description":"Macro region","title":"Region"},"description":"Macro region"},{"name":"kind","in":"query","required":false,"schema":{"type":"string","description":"One of: substations, lines, generators","default":"substations","title":"Kind"},"description":"One of: substations, lines, generators"},{"name":"min_kv","in":"query","required":false,"schema":{"type":"integer","description":"Voltage filter (kV) for substations/lines","default":0,"title":"Min Kv"},"description":"Voltage filter (kV) for substations/lines"},{"name":"min_mw","in":"query","required":false,"schema":{"type":"number","description":"Capacity filter (MW) for generators","default":0.0,"title":"Min Mw"},"description":"Capacity filter (MW) for generators"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":1,"default":2000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Transmission Geojson V1 Site Intelligence Transmission Geojson Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/quality/probes/ingest":{"post":{"tags":["quality-of-service"],"summary":"Probes Ingest","description":"Ingest a batch of Class C edge-vantage probes from Cloudflare Workers.\n\nReturns inserted-row count + a request id (collection_run_id echo)\nso the Worker can log a one-line success.","operationId":"probes_ingest_v1_quality_probes_ingest_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProbeIngestRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Probes Ingest V1 Quality Probes Ingest Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/quality/providers":{"get":{"tags":["quality-of-service"],"summary":"List Providers","description":"Provider universe with regions + tracked SKUs.","operationId":"list_providers_v1_quality_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Providers V1 Quality Providers Get"}}}}}}},"/v1/quality/preview":{"get":{"tags":["quality-of-service"],"summary":"Quality Preview","description":"Combined per-provider scorecard — drives the F13 dashboard cards.\n\nReturns one entry per provider with API/CAP/NET/SLA roll-ups so the\ndashboard renders one card per provider in a single request.\n\nAll values are PREVIEW tier — see methodology AVAILABILITY-V1 §5.","operationId":"quality_preview_v1_quality_preview_get","parameters":[{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Single provider; omit for all","title":"Provider"},"description":"Single provider; omit for all"},{"name":"window_h","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"description":"Lookback window for AVAIL-API","default":24,"title":"Window H"},"description":"Lookback window for AVAIL-API"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Quality Preview V1 Quality Preview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/quality/data-freshness":{"get":{"tags":["quality-of-service"],"summary":"Data Freshness","description":"Per-table last-updated timestamps + recent row counts.\n\nPowers the F5 ATLAS \"data freshness\" overlay so partners see at\na glance which underlying data layers are fresh vs stale. Reads\ncheap COUNT/MAX queries on the live tables — single roundtrip.","operationId":"data_freshness_v1_quality_data_freshness_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Data Freshness V1 Quality Data Freshness Get"}}}}}}},"/v1/quality/vantage-coverage":{"get":{"tags":["quality-of-service"],"summary":"Vantage Coverage","description":"Per-provider vantage-class coverage matrix — confidence inputs.\n\nAggregates the *four* QoS tables that carry vantage_class:\n  A → provider_network_latency        (always 'A')\n  B/C → provider_probes               (vantage_class column)\n  D/E → provider_incident_events      (vantage_class column)\n\nReturns one row per provider with a list of present classes plus\nsample counts. F13's \"Vantage class coverage\" panel visualizes\nthis as a 5-column grid (one cell per class A/B/C/D/E).\n\nConfidence_tier per AVAILABILITY-V1 §5: ≥4 classes → full · ≥3 →\nreduced · <3 → preview.","operationId":"vantage_coverage_v1_quality_vantage_coverage_get","parameters":[{"name":"window_h","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"description":"Lookback window in hours","default":24,"title":"Window H"},"description":"Lookback window in hours"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Vantage Coverage V1 Quality Vantage Coverage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/quality/methodology":{"get":{"tags":["quality-of-service"],"summary":"Methodology","description":"Inline summary of the methodology spec for the dashboard.","operationId":"methodology_v1_quality_methodology_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Methodology V1 Quality Methodology Get"}}}}}}},"/v1/filings/companies-house":{"get":{"tags":["filings"],"summary":"Companies House Filings","description":"Recent UK Companies House filings for the GPU-operator watchlist.\n\nFilterable by provider slug and CH filing-type code. Default sort\nis filing_date DESC so the most recent accounts surface first.","operationId":"companies_house_filings_v1_filings_companies_house_get","parameters":[{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by internal slug","title":"Provider"},"description":"Filter by internal slug"},{"name":"filing_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CH filing type code (AA / CS01 / MR01 / ...)","title":"Filing Type"},"description":"CH filing type code (AA / CS01 / MR01 / ...)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/filings/lei":{"get":{"tags":["filings"],"summary":"Lei Records","description":"Global LEI records resolved for FLOPS-tracked GPU operators.\n\nSparse-by-design — many providers don't have LEIs. Empty rows for\na provider means \"no GLEIF presence\", which is itself a meaningful\ncounterparty signal (LEIs gate derivative settlement).","operationId":"lei_records_v1_filings_lei_get","parameters":[{"name":"provider","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Provider"}},{"name":"jurisdiction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO code, e.g. 'US' or 'GB'","title":"Jurisdiction"},"description":"ISO code, e.g. 'US' or 'GB'"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/filings/edgar":{"get":{"tags":["filings"],"summary":"Edgar Filings","description":"US SEC EDGAR XBRL company-facts (migration 020).\n\nOne row per (ticker, concept, period_end). Default sort puts the\nmost recent period first per ticker, breaking ties by descending\nUSD value so the largest capex / PPE / real-estate datapoints\nsurface at the top.","operationId":"edgar_filings_v1_filings_edgar_get","parameters":[{"name":"ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by ticker (e.g. 'NVDA')","title":"Ticker"},"description":"Filter by ticker (e.g. 'NVDA')"},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"capex | ppe | real_estate","title":"Kind"},"description":"capex | ppe | real_estate"},{"name":"form_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"10-K / 10-Q / 20-F","title":"Form Type"},"description":"10-K / 10-Q / 20-F"},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO date — only return period_end >= this","title":"Since"},"description":"ISO date — only return period_end >= this"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/filings/entity/{provider}":{"get":{"tags":["filings"],"summary":"Entity Resolution","description":"Cross-source entity resolution for one provider.\n\nReturns the LEI record + most recent UK CH filings + parent-LEI\nedges so a partner can see the full corporate identity behind a\nFLOPS counterparty in one call.","operationId":"entity_resolution_v1_filings_entity__provider__get","parameters":[{"name":"provider","in":"path","required":true,"schema":{"type":"string","title":"Provider"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/filings/coverage":{"get":{"tags":["filings"],"summary":"Coverage Summary","description":"Per-jurisdiction filings-coverage summary for the dashboard panel.\n\nReturns count of distinct providers with each kind of coverage:\n  - uk_companies_house  — has at least one CH filing\n  - global_lei          — has a resolved LEI\n  - both                — covered in both sources\n\nDesigned to back a small \"FILINGS COVERAGE\" panel on the dashboard\n— same shape as the QoS vantage-coverage panel.","operationId":"coverage_summary_v1_filings_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/filings/entities":{"get":{"tags":["filings"],"summary":"Entities List","description":"Canonical entity-resolved provider list backing the F12 filings panel.\n\nReads `provider_entity_resolved` (migration 013) so each row is one\ncanonical provider with cross-source fields denormalised. Frontend\ncan render a single table without joining client-side.","operationId":"entities_list_v1_filings_entities_get","parameters":[{"name":"has_uk","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter to providers with UK CH filings","title":"Has Uk"},"description":"Filter to providers with UK CH filings"},{"name":"has_lei","in":"query","required":false,"schema":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Filter to providers with a resolved LEI","title":"Has Lei"},"description":"Filter to providers with a resolved LEI"},{"name":"jurisdiction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO code prefix, e.g. 'US' or 'GB'","title":"Jurisdiction"},"description":"ISO code prefix, e.g. 'US' or 'GB'"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"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"}}}}}}},"/v1/custom-indices/contract":{"get":{"tags":["custom-indices"],"summary":"Contract","description":"Surfaces the validator contract: canonical SKU list + allowed enums.\n\nPartners hit this once to learn what filters/cadences/aggregation\nmethods are permitted before submitting a spec to /validate.","operationId":"contract_v1_custom_indices_contract_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Contract V1 Custom Indices Contract Get"}}}}}}},"/v1/custom-indices/validate":{"post":{"tags":["custom-indices"],"summary":"Validate","description":"Validate a spec without persisting. Returns the canonical form +\ndeterministic spec_hash so partners can confirm the hash they'd see\non a published tick before they actually create the index.","operationId":"validate_v1_custom_indices_validate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Validate V1 Custom Indices Validate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/specs":{"get":{"tags":["custom-indices"],"summary":"List Specs","description":"List custom index specs.\n\nPublic specs are visible to anyone; private specs are owner_org-scoped\n(the API gateway / auth middleware is expected to enforce that — this\nrouter doesn't do per-org auth).","operationId":"list_specs_v1_custom_indices_specs_get","parameters":[{"name":"owner_org","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by owner_org","title":"Owner Org"},"description":"Filter by owner_org"},{"name":"visibility","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"private|public|partner","title":"Visibility"},"description":"private|public|partner"},{"name":"is_active","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Is Active"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Specs V1 Custom Indices Specs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/_admin/pending-approvals":{"get":{"tags":["custom-indices"],"summary":"Pending Approvals","description":"List public-visibility specs awaiting approval. Per migration 016\n+ the council k-anon governance, public specs require an\napproved_by + approved_at to surface in /specs?visibility=public.\nUntil that's set, the publisher computes ticks (so the spec owner\ncan sanity-check) but the spec is not advertised externally.\n\nDefined BEFORE /{index_id} so '_admin' doesn't shadow.","operationId":"pending_approvals_v1_custom_indices__admin_pending_approvals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Pending Approvals V1 Custom Indices  Admin Pending Approvals Get"}}}}}}},"/v1/custom-indices/_admin/activate":{"post":{"tags":["custom-indices"],"summary":"Activate Spec","description":"Flip a spec's is_active flag.\n\nThe publisher loop only ticks active specs; freshly-created specs\narrive with is_active=False until an operator explicitly enables\nthem. Idempotent: re-activating an already-active spec just bumps\nupdated_at.","operationId":"activate_spec_v1_custom_indices__admin_activate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ActivateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Activate Spec V1 Custom Indices  Admin Activate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/_admin/approve":{"post":{"tags":["custom-indices"],"summary":"Approve Spec","description":"Approve a public-visibility spec for external advertisement.\nSets approved_by + approved_at = NOW(). Idempotent: re-approving\nan already-approved spec just bumps approved_at + updates the\napprover name.","operationId":"approve_spec_v1_custom_indices__admin_approve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Approve Spec V1 Custom Indices  Admin Approve Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/_movers":{"get":{"tags":["custom-indices"],"summary":"Movers","description":"Top N custom indices by recent price change. Powers the F14\n'TRENDING / LAUNCHPAD MOVERS' panel — finance users see at a glance\nwhich user-defined indices are actually moving without scrolling\nthe full saved-indices table.\n\nReturns two lists: gainers (largest +%) and losers (largest -%),\neach with current price, prior price, delta_pct, num_sources,\nand last_computed_at. Specs without a tick-pair in the window are\nexcluded — needs at least 2 ticks to compute change.\n\nDefined BEFORE /{index_id} so '_movers' doesn't shadow.","operationId":"movers_v1_custom_indices__movers_get","parameters":[{"name":"window_h","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"description":"Lookback window in hours for change calc","default":24,"title":"Window H"},"description":"Lookback window in hours for change calc"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"description":"Max specs to return per direction","default":10,"title":"Limit"},"description":"Max specs to return per direction"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Movers V1 Custom Indices  Movers Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/_publisher/stats":{"get":{"tags":["custom-indices"],"summary":"Publisher Stats","description":"Snapshot of the async publisher loop's running counters. Surfaces:\niterations / ticks_published / ticks_skipped / ticks_errored / started_at\n/ last_iteration_at / last_iteration_due. Used to confirm the loop is\nalive in production without tailing fly logs.\n\nDefined BEFORE /{index_id} so 'index_id == \"_publisher\"' doesn't shadow\nthis route — FastAPI matches in declaration order.","operationId":"publisher_stats_v1_custom_indices__publisher_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Publisher Stats V1 Custom Indices  Publisher Stats Get"}}}}}}},"/v1/custom-indices/_admin/cleanup":{"post":{"tags":["custom-indices"],"summary":"Cleanup Specs","description":"Bulk cleanup of pollution / abandoned specs.\n\nDefined BEFORE /{index_id} so '_admin' doesn't shadow that route. The\nendpoint is auth-gated (BasicAuthMiddleware applies to everything under\n/v1) so casual API users can't wipe specs.\n\nDefault behavior is dry_run + soft-delete + unready-only — three safety\nlayers stacked. The frontend's bulk-cleanup button (when added) should\nshow the dry-run kill list first and require explicit \"destroy\" click.","operationId":"cleanup_specs_v1_custom_indices__admin_cleanup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CleanupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Cleanup Specs V1 Custom Indices  Admin Cleanup Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}":{"get":{"tags":["custom-indices"],"summary":"Get Spec","description":"Single spec, full payload. spec_json is returned so a partner\ncan verify the canonical form and recompute the spec_hash locally.","operationId":"get_spec_v1_custom_indices__index_id__get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Spec V1 Custom Indices  Index Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["custom-indices"],"summary":"Delete Spec","description":"Soft-delete: mark the spec inactive. Existing custom_index_values\nrows are preserved for audit (lineage). Hard delete only via direct\nDB access during NDA termination workflow.","operationId":"delete_spec_v1_custom_indices__index_id__delete","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Spec V1 Custom Indices  Index Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/latest":{"get":{"tags":["custom-indices"],"summary":"Latest","description":"Most recent computed tick for a custom index.","operationId":"latest_v1_custom_indices__index_id__latest_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Latest V1 Custom Indices  Index Id  Latest Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/history":{"get":{"tags":["custom-indices"],"summary":"History","description":"Range of ticks for a custom index. ISO timestamps; from/to inclusive.","operationId":"history_v1_custom_indices__index_id__history_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response History V1 Custom Indices  Index Id  History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/proof/{ts}":{"get":{"tags":["custom-indices"],"summary":"Proof","description":"Lineage hash + computation proof for a specific tick.\n\n``ts`` is the computed_at timestamp (ISO-8601). Returns the spec_hash\n+ observations_hash + lineage_hash so a partner can rerun the publisher\nagainst archived raw_pricing rows and reproduce the value.","operationId":"proof_v1_custom_indices__index_id__proof__ts__get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}},{"name":"ts","in":"path","required":true,"schema":{"type":"string","title":"Ts"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Proof V1 Custom Indices  Index Id  Proof  Ts  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices":{"post":{"tags":["custom-indices"],"summary":"Create Spec","description":"Register a new custom index spec. Validates the spec, computes\nspec_hash, and inserts into custom_index_spec.\n\nPublic visibility specs are inserted with approved_by NULL — they\nwon't surface in /specs?visibility=public until an index-board\nmember updates approved_by + approved_at out-of-band. Private specs\nskip approval (owner-scoped, not published).","operationId":"create_spec_v1_custom_indices_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSpecRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Spec V1 Custom Indices Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/recompute":{"post":{"tags":["custom-indices"],"summary":"Recompute Spec","description":"Compute a tick on-demand. Thin wrapper around compute_and_publish_tick;\nthe async publisher loop calls the same shared function on its cron.","operationId":"recompute_spec_v1_custom_indices__index_id__recompute_post","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Recompute Spec V1 Custom Indices  Index Id  Recompute Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/sensitivity":{"post":{"tags":["custom-indices"],"summary":"Sensitivity","description":"What-if recompute that drops one or more providers. Doesn't write to\ncustom_index_values — purely a scenario analysis. Returns baseline +\nscenario value + delta so the dashboard can render side-by-side.\n\nInspired by SpiderRock-style scenario tools. Lets a desk show a\ncounterparty 'this index settles fine even without your data', or\nflag 'losing X moves the value 8% — material concentration risk'.","operationId":"sensitivity_v1_custom_indices__index_id__sensitivity_post","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SensitivityRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sensitivity V1 Custom Indices  Index Id  Sensitivity Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/backtest":{"post":{"tags":["custom-indices"],"summary":"Backtest Spec","description":"Run an unsaved spec against the past N days of observations.\n\nReturns: {points: [{ts, value, num_sources}, ...], days, bucket_seconds}.\nInspired by SpiderRock scenario tools — lets a desk see the pattern\nBEFORE saving the spec or committing to a settlement.\n\nNOTE: scans raw_pricing.timestamp grouped by bucket, then aggregates\nper bucket. This is cheap at our scale (~3700 obs/hour × 7d = 600k\nrows × indexed scan); the publisher-worker async path replaces this\nwith precomputed historical ticks once buildout #8 lands.","operationId":"backtest_spec_v1_custom_indices_backtest_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BacktestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Backtest Spec V1 Custom Indices Backtest Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/methodology":{"get":{"tags":["custom-indices"],"summary":"Methodology Card","description":"One-page methodology summary for OTC counterparty negotiation.\nURL is shareable: counterparties reference flopsindex.com/methodology/{spec_hash}\nin their ISDA contracts. Inspired by Platts methodology PDFs.\n\nReturns a JSON document the frontend renders as a printable page;\nevery field maps to a clause counterparties want before settling.","operationId":"methodology_card_v1_custom_indices__index_id__methodology_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Methodology Card V1 Custom Indices  Index Id  Methodology Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/custom-indices/{index_id}/contract-snippet":{"get":{"tags":["custom-indices"],"summary":"Contract Snippet","description":"Drop-in OTC contract reference for the given index at the given tick.\nReturns both a JSON snippet and a human-readable text block partners can\npaste into their ISDA / bilateral contract templates. Inspired by\nHalcyon contract-building flows.\n\nIf `ts` is omitted, uses the most recent tick.","operationId":"contract_snippet_v1_custom_indices__index_id__contract_snippet_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}},{"name":"ts","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ts"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Contract Snippet V1 Custom Indices  Index Id  Contract Snippet Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sku/canonical":{"get":{"tags":["partner-sku"],"summary":"List Canonical Skus","description":"List every canonical SKU. Partner-tier; X-FLOPS-Api-Key required.\n\nShape: ``{count, skus: [{canonical_id, model, variant, memory_gb,\ncompute_factor, memory_factor, interconnect_factor}]}``.","operationId":"list_canonical_skus_v1_sku_canonical_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sku/canonical/{canonical_id}":{"get":{"tags":["partner-sku"],"summary":"Get Canonical Sku","description":"Lookup one canonical SKU. 404 if not found.","operationId":"get_canonical_sku_v1_sku_canonical__canonical_id__get","parameters":[{"name":"canonical_id","in":"path","required":true,"schema":{"type":"string","title":"Canonical Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sku/resolve":{"get":{"tags":["partner-sku"],"summary":"Resolve Sku","description":"Resolve (model, variant, memory_gb [, source]) to canonical_id.\n\nWalks the same lookup chain as SpotPriceBuildv0's sku_registry:\n  1. alias exact match on (alias_string, source) when source given\n  2. canonical_id direct hit on (model, variant, memory_gb)\n  3. canonical_id loose hit on model only\n\nReturns the matched canonical SKU or 404.","operationId":"resolve_sku_v1_sku_resolve_get","parameters":[{"name":"model","in":"query","required":true,"schema":{"type":"string","title":"Model"}},{"name":"variant","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Variant"}},{"name":"memory_gb","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Memory Gb"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sku/aliases":{"get":{"tags":["partner-sku"],"summary":"List Aliases","description":"List every alias that maps to a canonical_id, plus the org\nthat registered each one.","operationId":"list_aliases_v1_sku_aliases_get","parameters":[{"name":"canonical_id","in":"query","required":true,"schema":{"type":"string","title":"Canonical Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sku/alias":{"post":{"tags":["partner-sku"],"summary":"Register Alias","description":"Register a new alias for an existing canonical_id.\n\nTagged with the caller's org_id so multiple partners can submit\noverlapping alias strings without colliding. Idempotent — re-\nregistering the same (canonical_id, alias, source, org_id) tuple\nreturns the existing row.","operationId":"register_alias_v1_sku_alias_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterAliasRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/partner/{org_slug}/health":{"get":{"tags":["partner"],"summary":"Partner Health","description":"Auth-gated ack: org exists + is active + has the supplied key.","operationId":"partner_health_v1_partner__org_slug__health_get","parameters":[{"name":"org_slug","in":"path","required":true,"schema":{"type":"string","title":"Org Slug"}},{"name":"X-Partner-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Partner-Api-Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Partner Health V1 Partner  Org Slug  Health Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/partner/{org_slug}/capacity":{"post":{"tags":["partner"],"summary":"Ingest Capacity","description":"Append a batch of capacity observations into the partner feed.\n\nAuth + path-org-match enforced. Bad observation rows are skipped\n(not 400'd) so a partner with one malformed record still gets the\nrest accepted; the response includes inserted/rejected counts and\nthe first error message for diagnostics.\n\nHMAC verification: X-Flops-Signature: sha256=<hex> over\ntimestamp + '.' + raw_body, signed with the partner's\nwebhook_signing_secret (migration 013). X-Flops-Timestamp is bound\ninto the signature so captured pairs can't be replayed with a fresh\ntimestamp; 5-min skew window. Behavior:\n  PARTNER_SIGNATURE_ENFORCEMENT=warn    — missing/invalid signatures\n    fire a security_event but the request is accepted. Default during\n    the 30d transition window.\n  PARTNER_SIGNATURE_ENFORCEMENT=enforce — missing/invalid → 401.","operationId":"ingest_capacity_v1_partner__org_slug__capacity_post","parameters":[{"name":"org_slug","in":"path","required":true,"schema":{"type":"string","title":"Org Slug"}},{"name":"X-Partner-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Partner-Api-Key"}},{"name":"X-Flops-Signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Flops-Signature"}},{"name":"X-Flops-Timestamp","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Flops-Timestamp"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityIngestRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Ingest Capacity V1 Partner  Org Slug  Capacity Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/receipt/latest":{"get":{"summary":"Get Latest Audit Receipt","description":"Return the audit receipt for the most recent oracle_prices row\nmatching (gpu_model, index_type).\n\nUsed by F15 Reference tab to show a per-index verification packet\nwithout the caller having to know the receipt_id. Mirrors the\nsemantics of ``GET /v1/audit/receipt/{receipt_id}`` (auth-free,\nsame JSON shape) but picks the latest row for the requested\n(gpu_model, index_type) pair.","operationId":"get_latest_audit_receipt_v1_audit_receipt_latest_get","parameters":[{"name":"gpu_model","in":"query","required":true,"schema":{"type":"string","title":"Gpu Model"}},{"name":"index_type","in":"query","required":false,"schema":{"type":"string","default":"on_demand","title":"Index Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Latest Audit Receipt V1 Audit Receipt Latest Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/receipt/{receipt_id}":{"get":{"summary":"Get Audit Receipt","description":"Compose + verify a covenant-eligibility receipt by id.\n\nLookup precedence:\n\n1. UUID receipt_id → try ``inputs_receipts`` first. If a\n   direct-written row exists, use it (composed_synthetic=False,\n   so guarantee 4 can flip to pass on a signed row).\n2. UUID receipt_id with no inputs_receipts row → fall through\n   to scanning oracle_prices for a UUID5 match (synthetic compose).\n3. Numeric id → oracle_prices.id lookup (synthetic compose).","operationId":"get_audit_receipt_v1_audit_receipt__receipt_id__get","parameters":[{"name":"receipt_id","in":"path","required":true,"schema":{"type":"string","title":"Receipt Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Audit Receipt V1 Audit Receipt  Receipt Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/receipt/{hash}/verify":{"get":{"summary":"Verify Receipt By Hash","description":"Verify that an observations_hash corresponds to a published row.\n\nReturns 200 in BOTH the verified and the not-verified cases. The\ncontract is \"give me a yes-or-no answer to: is this hash real?\"\n— a 404 would conflate \"unknown hash\" with \"endpoint missing\",\nbreaking the simple yes/no semantic that lender contracts\nreference.\n\nResponse shape (verified):\n    {\n      \"verified\": true,\n      \"hash\": \"<observations_hash>\",\n      \"receipt\": {\n          \"receipt_id\": ..., \"oracle_price_id\": ...,\n          \"index_id\": ..., \"gpu_model\": ..., \"index_type\": ...,\n          \"published_value\": ..., \"published_at\": ...,\n          \"methodology_version\": ...,\n          \"num_sources_kept\": ..., \"num_sources_raw\": ...,\n          \"observations_hash\": ...,\n          \"frozen\": ..., \"composed_synthetic\": ...\n      },\n      \"signature_verified\": true|false|null,\n      \"verification\": { ...full 7-guarantee report... }\n    }\n\nResponse shape (not verified):\n    {\n      \"verified\": false,\n      \"hash\": \"<as-supplied>\",\n      \"reason\": \"no published row carries this observations_hash\"\n    }\n\n503 only when the audit DB pool itself is unavailable — a\ntransient infrastructure condition, not a verification answer.","operationId":"verify_receipt_by_hash_v1_receipt__hash__verify_get","parameters":[{"name":"hash","in":"path","required":true,"schema":{"type":"string","title":"Hash"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Verify Receipt By Hash V1 Receipt  Hash  Verify Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/recompute":{"get":{"tags":["audit"],"summary":"List Recompute Receipts","description":"List recent recompute receipts. Filters: methodology_id,\nindex_id, status, since_hours (default 7d). Limit defaults to 100.\n\nReturns ``{count, receipts: [...]}``. Empty list when migration\n030 hasn't been applied (rather than 503) — lets the dashboard\nquery before/after the migration without breaking.","operationId":"list_recompute_receipts_v1_audit_recompute_get","parameters":[{"name":"methodology_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Methodology Id"}},{"name":"index_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Index Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(green|yellow|red|unknown)$"},{"type":"null"}],"title":"Status"}},{"name":"since_hours","in":"query","required":false,"schema":{"type":"integer","maximum":2160,"minimum":1,"default":168,"title":"Since Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Recompute Receipts V1 Audit Recompute Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/recompute/_status":{"get":{"tags":["audit"],"summary":"Recompute Status","description":"Health summary — counts per status over the window.\nDefault 7-day window. Used for dashboard freshness banners.","operationId":"recompute_status_v1_audit_recompute__status_get","parameters":[{"name":"since_hours","in":"query","required":false,"schema":{"type":"integer","maximum":2160,"minimum":1,"default":168,"title":"Since Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Recompute Status V1 Audit Recompute  Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/audit/recompute/{run_id}":{"get":{"tags":["audit"],"summary":"Get Recompute Receipt","description":"Fetch one receipt by run_id (UUID). 404 when absent.","operationId":"get_recompute_receipt_v1_audit_recompute__run_id__get","parameters":[{"name":"run_id","in":"path","required":true,"schema":{"type":"string","title":"Run Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Recompute Receipt V1 Audit Recompute  Run Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/derived/compute-margin":{"get":{"tags":["derived"],"summary":"Compute Margin","description":"One-point compute-margin computation.\n\nPulls live FLCI price + power-cost components from oracle_prices\nwhen DSN available; degrades to seed tariffs otherwise. Returns\nfull decomposition so the caller can sanity-check.","operationId":"compute_margin_v1_derived_compute_margin_get","parameters":[{"name":"sku","in":"query","required":true,"schema":{"type":"string","description":"GPU SKU slug, e.g. 'h100_sxm5'","title":"Sku"},"description":"GPU SKU slug, e.g. 'h100_sxm5'"},{"name":"region","in":"query","required":false,"schema":{"type":"string","description":"region slug, e.g. 'us_west'","default":"us_east","title":"Region"},"description":"region slug, e.g. 'us_west'"},{"name":"pue","in":"query","required":false,"schema":{"type":"number","maximum":2.5,"exclusiveMinimum":1.0,"description":"datacenter PUE; default 1.3","default":1.3,"title":"Pue"},"description":"datacenter PUE; default 1.3"},{"name":"kwh_source","in":"query","required":false,"schema":{"type":"string","pattern":"^(live_lmp|tariff_seed|basis_adjusted)$","description":"$/kWh data source","default":"live_lmp","title":"Kwh Source"},"description":"$/kWh data source"},{"name":"kwh_override","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":1.0,"exclusiveMinimum":0.0},{"type":"null"}],"description":"explicit $/kWh override","title":"Kwh Override"},"description":"explicit $/kWh override"},{"name":"rack_amortization","in":"query","required":false,"schema":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"description":"$/GPU-hr datacenter capex amortization","title":"Rack Amortization"},"description":"$/GPU-hr datacenter capex amortization"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Compute Margin V1 Derived Compute Margin Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/derived/compute-margin/timeseries":{"get":{"tags":["derived"],"summary":"Compute Margin Timeseries","description":"30d compute-margin timeseries. Joins FLCI history × live LMP\nhistory hour-by-hour. tariff_seed kwh source only at v0.9 (live-\nLMP-per-hour join is in the v1.0 backlog — adds significant\ncomplexity for marginal accuracy improvement).","operationId":"compute_margin_timeseries_v1_derived_compute_margin_timeseries_get","parameters":[{"name":"sku","in":"query","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"region","in":"query","required":false,"schema":{"type":"string","default":"us_east","title":"Region"}},{"name":"pue","in":"query","required":false,"schema":{"type":"number","maximum":2.5,"exclusiveMinimum":1.0,"default":1.3,"title":"Pue"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","pattern":"^(24h|7d|30d|90d)$","default":"7d","title":"Range"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Compute Margin Timeseries V1 Derived Compute Margin Timeseries Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/derived/spread":{"get":{"tags":["derived"],"summary":"Spread","description":"Time-aligned spread between two index legs.\n\nBuilds an hourly series per leg (last value per hour, k-anon >= 3),\ninner-joins on the hour bucket, and returns spread = A − B (or A/B\nwhen mode=ratio). Stats block summarizes the spread series:\ncurrent, mean, stdev, p25/p50/p75, z-score-now, percentile-rank.\n\nUse the z-score / percentile to gauge whether a current spread is\nrich or cheap vs its own history.","operationId":"spread_v1_derived_spread_get","parameters":[{"name":"a_gpu_model","in":"query","required":true,"schema":{"type":"string","description":"leg A gpu_model / model id (e.g. 'H100')","title":"A Gpu Model"},"description":"leg A gpu_model / model id (e.g. 'H100')"},{"name":"a_index_type","in":"query","required":true,"schema":{"type":"string","description":"leg A index_type (e.g. 'on_demand')","title":"A Index Type"},"description":"leg A index_type (e.g. 'on_demand')"},{"name":"b_gpu_model","in":"query","required":true,"schema":{"type":"string","description":"leg B gpu_model / model id","title":"B Gpu Model"},"description":"leg B gpu_model / model id"},{"name":"b_index_type","in":"query","required":true,"schema":{"type":"string","description":"leg B index_type","title":"B Index Type"},"description":"leg B index_type"},{"name":"range","in":"query","required":false,"schema":{"type":"string","pattern":"^(24h|7d|30d|90d|180d)$","default":"30d","title":"Range"}},{"name":"mode","in":"query","required":false,"schema":{"type":"string","pattern":"^(absolute|ratio)$","description":"spread = A-B (absolute) or A/B (ratio)","default":"absolute","title":"Mode"},"description":"spread = A-B (absolute) or A/B (ratio)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Spread V1 Derived Spread Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/derived/spread/presets":{"get":{"tags":["derived"],"summary":"Spread Presets","description":"Curated spread presets — the dashboard renders these as quick-pick\nbuttons. Keys are stable; labels are display-only.","operationId":"spread_presets_v1_derived_spread_presets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Spread Presets V1 Derived Spread Presets Get"}}}}}}},"/v1/admin/users":{"post":{"tags":["admin"],"summary":"Create User","description":"Create one partner_users row under an existing partner_org.\nReturns the freshly-generated API key + dashboard URL ONCE.\nSubsequent calls have no way to recover the plaintext.","operationId":"create_user_v1_admin_users_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create User V1 Admin Users Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Users","description":"List partner_users rows. Filter by partner_slug to scope to one org.","operationId":"list_users_v1_admin_users_get","parameters":[{"name":"partner_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Users V1 Admin Users Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}":{"patch":{"tags":["admin"],"summary":"Deactivate User","description":"Soft-revoke: flip is_active=FALSE. The API key + dashboard\ncredential immediately stop working. No hard-delete because the\napi_request_log rows FK back to user_id — preserving the audit\ntrail is the whole point.","operationId":"deactivate_user_v1_admin_users__user_id__patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Deactivate User V1 Admin Users  User Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/public-traffic":{"get":{"tags":["admin"],"summary":"Public Traffic Summary","description":"Aggregate agent traffic on the Track-D public surface.\n\nThe auth middleware fires-and-forgets a row into api_request_log\nfor every hit on /v1/price, /v1/ts, /v1/search, /v1/verify, /i/...,\n/v1/audit/receipt/..., /.well-known/agent.json, /v2/catalog/public,\n/llms.txt, /cite — with partner_org_id=NULL + auth_method='public'.\n\nThis endpoint folds those rows into the panels the admin dashboard\nwants: total hits, unique IPs, unique user-agents, top slugs,\ntop paths, top UAs, and an hourly bucketed time series.\n\nBounded at 168h (7 days) so an admin can't accidentally scan all\n90 days of retention and OOM the DB connection.","operationId":"public_traffic_summary_v1_admin_public_traffic_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Traffic Summary V1 Admin Public Traffic Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/reactivate":{"patch":{"tags":["admin"],"summary":"Reactivate User","description":"Un-revoke a user. Reverses deactivate_user. The parent org must\nstill be active — otherwise we'd silently restore credentials that\ncan't actually be used (the cookie lookup chain requires\npo.is_active=TRUE).","operationId":"reactivate_user_v1_admin_users__user_id__reactivate_patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Reactivate User V1 Admin Users  User Id  Reactivate Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/rotate-password":{"post":{"tags":["admin"],"summary":"Rotate User Password","description":"Generate a new dashboard password for the user and return the\nplaintext ONCE. The bcrypt hash replaces the old one in the same\ntransaction. If the user previously had no dashboard_password_hash\n(API-only), this endpoint mints one — i.e. it can also be used to\npromote an API-only user to dashboard access.\n\nThe API key is NOT affected. Call rotate-api-key separately if you\nwant to rotate that too.","operationId":"rotate_user_password_v1_admin_users__user_id__rotate_password_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Rotate User Password V1 Admin Users  User Id  Rotate Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/profile":{"patch":{"tags":["admin"],"summary":"Edit User Profile","description":"Edit any subset of scope/rate_limit_rpm/display_name/email/notes\non a partner_users row. Fields omitted from the request body stay\nunchanged. Cache-invalidates the user so the change takes effect\non the very next request.\n\nUse this instead of revoke+recreate when a customer upgrades scope,\nchanges email, or you need to bump their rate limit. For password\nchanges use the dedicated set-password / rotate-password endpoints;\nfor revoke/reactivate use the PATCH .../ + reactivate endpoints.","operationId":"edit_user_profile_v1_admin_users__user_id__profile_patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditUserRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Edit User Profile V1 Admin Users  User Id  Profile Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}/profile":{"patch":{"tags":["admin"],"summary":"Edit Partner Profile","description":"Same as edit_user_profile but for partner_org. Lets you assign\ncommercial_tier as customers upgrade their lightpaper package.\norg_slug is intentionally not editable — changing it would break\nlogin URLs and cached session cookies.","operationId":"edit_partner_profile_v1_admin_partners__partner_id__profile_patch","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EditPartnerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Edit Partner Profile V1 Admin Partners  Partner Id  Profile Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/set-password":{"post":{"tags":["admin"],"summary":"Set User Password","description":"Replace the user's dashboard password with the admin-supplied\nplaintext. Companion to rotate-password — rotate generates a random\nstring, set-password lets the admin pick the value (useful when\nreading a credential aloud during onboarding).\n\nSame audit-trail + bcrypt path as create_user / rotate_user_password.\nPlaintext is never written to disk or logged.","operationId":"set_user_password_v1_admin_users__user_id__set_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/SetPasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Set User Password V1 Admin Users  User Id  Set Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}/set-password":{"post":{"tags":["admin"],"summary":"Set Partner Password","description":"Replace the org-wide dashboard password with the admin-supplied\nplaintext. Companion to rotate-password — same as set_user_password\nbut for partner_org rows.","operationId":"set_partner_password_v1_admin_partners__partner_id__set_password_post","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetPasswordRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Set Partner Password V1 Admin Partners  Partner Id  Set Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}":{"patch":{"tags":["admin"],"summary":"Deactivate Partner","description":"Soft-revoke a partner_org. Sets is_active=FALSE on the org AND\ncascades to all partner_users under it, so the entire credential\nsurface stops working in one click. The org_slug stays uniqueness-\nblocked (the create endpoint's pre-insert check still sees the\ninactive row) — that's intentional so a revoked partner can't be\naccidentally re-created with the same slug.\n\nAudit trail preserved: api_request_log keeps FK references; soft\ndelete is the whole point.\n\nReturns the cascade count so the admin sees what they revoked.","operationId":"deactivate_partner_v1_admin_partners__partner_id__patch","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Deactivate Partner V1 Admin Partners  Partner Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}/reactivate":{"patch":{"tags":["admin"],"summary":"Reactivate Partner","description":"Un-revoke a partner_org. Reverses deactivate_partner — but does\nNOT cascade-reactivate users, because the admin may want to restore\nthe org while leaving specific users revoked. Use the per-user\nreactivate endpoint to bring each user back individually.","operationId":"reactivate_partner_v1_admin_partners__partner_id__reactivate_patch","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Reactivate Partner V1 Admin Partners  Partner Id  Reactivate Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}/rotate-password":{"post":{"tags":["admin"],"summary":"Rotate Partner Password","description":"Generate a new org-wide dashboard password. Returns plaintext\nONCE. Mints a password even if the org previously had none (API-\nonly org becomes dashboard-capable). API key not affected.","operationId":"rotate_partner_password_v1_admin_partners__partner_id__rotate_password_post","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Rotate Partner Password V1 Admin Partners  Partner Id  Rotate Password Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners":{"post":{"tags":["admin"],"summary":"Create Partner","description":"Create one partner_org row. Returns the org-wide API key + dashboard\ncredential ONCE. Most partners then call POST /v1/admin/users to mint\nper-person credentials nested under this org; the org-wide credential\nhere is a service-account fallback.","operationId":"create_partner_v1_admin_partners_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatePartnerRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Partner V1 Admin Partners Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["admin"],"summary":"List Partners","description":"List partner_org rows. Defaults to active-only — soft-revoked\nrows hidden so the create-user dropdown shows a clean list. Pass\n?include_inactive=true to see everything (audit / restore flow).","operationId":"list_partners_v1_admin_partners_get","parameters":[{"name":"include_inactive","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Inactive"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Partners V1 Admin Partners Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/webauthn/options":{"post":{"tags":["admin"],"summary":"Webauthn Register Options","description":"Issue WebAuthn registration options for a user. Returns the JSON\nthe browser feeds to navigator.credentials.create(). The signed\nstate cookie carries the challenge across to /register.","operationId":"webauthn_register_options_v1_admin_users__user_id__webauthn_options_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Webauthn Register Options V1 Admin Users  User Id  Webauthn Options Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/webauthn/register":{"post":{"tags":["admin"],"summary":"Webauthn Register","description":"Verify the attestation + persist the credential.","operationId":"webauthn_register_v1_admin_users__user_id__webauthn_register_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/WebauthnRegisterRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Webauthn Register V1 Admin Users  User Id  Webauthn Register Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/webauthn/list":{"get":{"tags":["admin"],"summary":"Webauthn List","description":"List enrolled passkeys for a user (label + age, not the key).","operationId":"webauthn_list_v1_admin_users__user_id__webauthn_list_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Webauthn List V1 Admin Users  User Id  Webauthn List Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/webauthn/{credential_pk}":{"delete":{"tags":["admin"],"summary":"Webauthn Revoke","description":"Revoke (hard-delete) one passkey. Lost device path.","operationId":"webauthn_revoke_v1_admin_users__user_id__webauthn__credential_pk__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}},{"name":"credential_pk","in":"path","required":true,"schema":{"type":"integer","title":"Credential Pk"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Webauthn Revoke V1 Admin Users  User Id  Webauthn  Credential Pk  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/users/{user_id}/unlock":{"post":{"tags":["admin"],"summary":"Unlock User","description":"Clear lockout on a partner_user. Resets failed_login_count=0 and\nlocked_until=NULL so the credential is immediately usable again.\nUse when a legit user forgot their password, ran out of guesses,\nand called you to unlock.","operationId":"unlock_user_v1_admin_users__user_id__unlock_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"integer","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Unlock User V1 Admin Users  User Id  Unlock Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/partners/{partner_id}/unlock":{"post":{"tags":["admin"],"summary":"Unlock Partner","description":"Clear lockout on a partner_org.","operationId":"unlock_partner_v1_admin_partners__partner_id__unlock_post","parameters":[{"name":"partner_id","in":"path","required":true,"schema":{"type":"integer","title":"Partner Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Unlock Partner V1 Admin Partners  Partner Id  Unlock Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/security-status":{"get":{"tags":["admin"],"summary":"Security Status","description":"Read-only snapshot of the security posture so the operator can\nconfirm hardening is active without grepping the codebase.\n\nSurfaces:\n- admin IP allowlist (parsed CIDRs + caller IP)\n- rate-limit + burst-counter thresholds\n- Sentry availability for burst alerts\n- operator-credential rotation age (if DASHBOARD_PASS_ROTATED_AT set)","operationId":"security_status_v1_admin_security_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Security Status V1 Admin Security Status Get"}}}}}}},"/v1/admin/auth-failures":{"get":{"tags":["admin"],"summary":"Auth Failures Snapshot","description":"Snapshot the in-process per-IP auth-failure counters.\n\nRead-only window into the rolling-60s failure tracker. Surfaces who's\ncurrently being noisy at the auth boundary. Sentry alerts fire\nautomatically when an IP crosses the threshold; this endpoint lets\nthe operator see the same data without leaving the admin terminal.\n\nReset semantics: counter trims itself as entries age out of the\nrolling window. To force-clear an entry, restart the Fly machine\n(counter is in-process, not persisted).","operationId":"auth_failures_snapshot_v1_admin_auth_failures_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Auth Failures Snapshot V1 Admin Auth Failures Get"}}}}}}},"/v1/admin/activity":{"get":{"tags":["admin"],"summary":"List Activity","description":"Query the api_request_log audit trail. Filters compose; defaults\nreturn the last 200 requests across all partners in the last 24h.\n\nFilters:\n  partner_slug   exact match on partner_org.org_slug\n  user_slug      exact match on partner_users.user_slug\n  endpoint       substring match on path (drill-down from USAGE tab\n                 Top Endpoints click)\n  run_id         exact match on caller_run_id (drill-down to all\n                 requests from a single agent fanout)","operationId":"list_activity_v1_admin_activity_get","parameters":[{"name":"partner_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner Slug"}},{"name":"user_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Slug"}},{"name":"endpoint","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Endpoint"}},{"name":"run_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Run Id"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Activity V1 Admin Activity Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/analytics/usage":{"get":{"tags":["admin"],"summary":"Analytics Usage","description":"Cross-cut analytics: who's hitting what, when.\n\nThree rollups in one round-trip so the admin tab can render the\nfull picture without N requests:\n  - by_user:     top users (request count + last_seen + p95 latency)\n  - by_endpoint: top paths (count, p50/p95 ms, error rate)\n  - by_period:   bucketed time series (hour buckets ≤48h, else day)\nReads from api_request_log which the auth middleware populates\nfire-and-forget on every authenticated request. 14d retention floor\nenforced by the nightly DELETE in app.py.","operationId":"analytics_usage_v1_admin_analytics_usage_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":168,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Analytics Usage V1 Admin Analytics Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/jobs/cleanup-sessions":{"post":{"tags":["admin"],"summary":"Cleanup Sessions Endpoint","description":"Drop tombstoned user_sessions + partner_sub_keys rows older than the\nretention window. Idempotent, fast (<1s on prod), admin-scope only.\n\nSchedule via a Fly daily scheduled machine pointed at\n``python -m src.jobs.cleanup_sessions``, or fire manually via this\nendpoint when an operator wants to compact after a bulk revoke.","operationId":"cleanup_sessions_endpoint_v1_admin_jobs_cleanup_sessions_post","parameters":[{"name":"retention_days","in":"query","required":false,"schema":{"type":"integer","default":90,"title":"Retention Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Cleanup Sessions Endpoint V1 Admin Jobs Cleanup Sessions Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/jobs/cleanup-request-log":{"post":{"tags":["admin"],"summary":"Cleanup Request Log Endpoint","description":"Trim api_request_log past the retention window. Mirror of\ncleanup-sessions: admin-scope, retention_days clamped [1, 3650],\nidempotent. The scheduled-machine cron at flops-cleanup invokes the\nsame `run_cleanup_request_log` via the wrapper script daily — this\nendpoint is for manual fire after a traffic spike or before a backup.","operationId":"cleanup_request_log_endpoint_v1_admin_jobs_cleanup_request_log_post","parameters":[{"name":"retention_days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Retention Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Cleanup Request Log Endpoint V1 Admin Jobs Cleanup Request Log Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/security-events":{"get":{"tags":["admin"],"summary":"List Security Events","description":"List rows from security_events (migration 011), DESC by occurred_at.\n\nFilters:\n  hours       — rolling window, clamped to [1, 720]  (default 24)\n  event_type  — exact match: login_failed, lockout_triggered,\n                admin_ip_blocked, webauthn_declined, sub_key_concurrency_429\n  severity    — exact match: info / warning / error\n  limit       — clamped to [1, 1000]  (default 100)\n\nAdmin-scope only — same guard as /v1/admin/activity. The payload\ncolumn is returned as a JSON object so admin UIs can render the\nper-event detail (caller IP for admin_ip_blocked, sub_key_id for\nthe 429, etc.) without a second query.","operationId":"list_security_events_v1_admin_security_events_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Type"}},{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Severity"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Security Events V1 Admin Security Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/security-events/summary":{"get":{"tags":["admin"],"summary":"Security Events Summary","description":"Aggregated counts for the /admin SECURITY tab counter cards.\n\nReturns:\n  hours        — echoed (clamped [1, 720])\n  total        — count(*) in the window\n  by_type      — {event_type: count} for ALL types observed\n  by_severity  — {severity: count} for ALL severities observed\n  top_ips      — top 10 [{ip, count}] by occurrence\n\nAll four event_type slugs the instrumentation emits today are\nsurfaced even when count=0 so the counter cards in the UI render\nconsistently between an empty window and a populated one.","operationId":"security_events_summary_v1_admin_security_events_summary_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Security Events Summary V1 Admin Security Events Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/security-events/types":{"get":{"tags":["admin"],"summary":"Security Events Types","description":"Canonical event-type catalog. Admin-scope only.\n\nThe dashboard's /admin SECURITY tab reads this on activation to\npopulate the event_type dropdown + the per-type counter cards.\nAdding a 6th instrumentation site means appending to\nsrc.security_events.EVENT_TYPES_CATALOG; the UI picks it up on the\nnext page load without an admin.html edit.\n\nThe `instrumented_at` field is informational — a static label naming\nwhen the canonical set was last expanded. `next_event_index` is the\n1-based next slot a new entry would land in, useful as an at-a-glance\n\"how many sites do we instrument?\" telemetry value.","operationId":"security_events_types_v1_admin_security_events_types_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Security Events Types V1 Admin Security Events Types Get"}}}}}}},"/v1/admin/iso-health":{"get":{"tags":["admin-iso-health"],"summary":"Iso Health","description":"Return the cache-health payload for the 11 ISO adapter caches.\n\nShape pinned by ``tests/data_feeds/test_iso_health.py``:\n\n  {\n    \"generated_at\": \"2026-05-19T...+00:00\",\n    \"iso_caches\": {\n      \"aeso\":  {\"hits\": ..., \"misses\": ..., \"stale_serves\": ..., ...},\n      ...\n      \"oasis\": {...},\n    },\n    \"summary\": {\n      \"adapter_count\":      11,\n      \"total_hits\":         ...,\n      \"total_misses\":       ...,\n      \"total_stale_serves\": ...,\n      \"total_fetch_failures\": ...,\n      \"any_stale\":          bool\n    }\n  }\n\nRe-runs the cache-stats sweep every request — no payload caching\non this surface. The 11 stats objects themselves are cheap counter\nreads; the cost is dwarfed by FastAPI's per-request overhead.","operationId":"iso_health_v1_admin_iso_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Iso Health V1 Admin Iso Health Get"}}}}}}},"/v1/admin/iso-health/{name}":{"get":{"tags":["admin-iso-health"],"summary":"Iso Health One","description":"Return the cache-stats slice + drill metadata for a single adapter.\n\nCompanion to ``GET /v1/admin/iso-health`` for the per-adapter modal\n(Lane 4 R4 Part D dashboard drill-down). Returns the same stats\npayload restricted to one adapter, plus a ``drill_metadata`` block\nnaming the next-action surfaces an operator might want.\n\nReturns 404 if the adapter name is not registered in the cache\nregistry. Admin-scope only (same gate as the rollup endpoint).","operationId":"iso_health_one_v1_admin_iso_health__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Iso Health One V1 Admin Iso Health  Name  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/iso-health/{name}/events":{"get":{"tags":["admin-iso-health"],"summary":"Iso Health Events","description":"Per-adapter event ring-buffer endpoint.\n\nReturns up to ``limit`` most-recent cache events (newest-first).\nEach event has ``ts`` (ISO-8601 UTC), ``kind`` (``hit``, ``miss``,\n``stale_serve``, or ``fetch_fail``), and a short ``detail`` line.\n\nErrors:\n  - 403 if the caller isn't admin-scope.\n  - 404 if no cache is registered under ``name`` (typo defence).\n  - 422 if ``limit`` parses as non-integer (FastAPI validation).\n\nNegative or zero ``limit`` returns an empty events list rather than\n422 — callers (the drill-down SPA) treat that as the no-data branch\nalready and we don't want the modal to render a JSON parse error\ninstead of an empty table.","operationId":"iso_health_events_v1_admin_iso_health__name__events_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Iso Health Events V1 Admin Iso Health  Name  Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/lender-outreach":{"get":{"tags":["admin","lender-outreach"],"summary":"List Outreach","description":"List pipeline rows, newest activity first.\n\nFilters:\n  - status: exact match against the enum\n  - partner: case-insensitive substring match\n  - limit: 1-500 (clamped)","operationId":"list_outreach_v1_admin_lender_outreach_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"partner","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Partner"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Outreach V1 Admin Lender Outreach Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin","lender-outreach"],"summary":"Create Outreach","description":"Create a new pipeline row. Re-pitches under a newer methodology\nversion should call this endpoint with the new version slug, NOT\nPATCH an existing row.","operationId":"create_outreach_v1_admin_lender_outreach_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOutreachRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Outreach V1 Admin Lender Outreach Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/lender-outreach/{outreach_id}":{"patch":{"tags":["admin","lender-outreach"],"summary":"Update Outreach","description":"Update a row. Only the supplied fields are touched (None = no-op).\nAlways bumps updated_at so the funnel-stall detector sees the activity.","operationId":"update_outreach_v1_admin_lender_outreach__outreach_id__patch","parameters":[{"name":"outreach_id","in":"path","required":true,"schema":{"type":"integer","title":"Outreach Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOutreachRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Outreach V1 Admin Lender Outreach  Outreach Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/lender-outreach/funnel":{"get":{"tags":["admin","lender-outreach"],"summary":"Funnel Summary","description":"Aggregate row counts per status. Drives the F16/F17 dashboard\nfunnel panel.","operationId":"funnel_summary_v1_admin_lender_outreach_funnel_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Funnel Summary V1 Admin Lender Outreach Funnel Get"}}}}}}},"/v1/contrib/intake":{"post":{"tags":["contrib","public"],"summary":"Create Intake","description":"Public partner-intake endpoint. Rate-limited via DB-window check.\n\nOn success, returns the public ticket_id (UUID) so the caller can\ncite it in follow-up email. NEVER returns the internal serial id or\ncaptured IP / user-agent.","operationId":"create_intake_v1_contrib_intake_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContribIntakeRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Create Intake V1 Contrib Intake Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/contrib-intake":{"get":{"tags":["admin","contrib"],"summary":"List Intakes","description":"Admin queue view. Filterable by status; newest first.","operationId":"list_intakes_v1_admin_contrib_intake_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Intakes V1 Admin Contrib Intake Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/contrib-intake/{ticket_id}":{"get":{"tags":["admin","contrib"],"summary":"Get Intake","operationId":"get_intake_v1_admin_contrib_intake__ticket_id__get","parameters":[{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string","title":"Ticket Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Intake V1 Admin Contrib Intake  Ticket Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["admin","contrib"],"summary":"Update Intake","operationId":"update_intake_v1_admin_contrib_intake__ticket_id__patch","parameters":[{"name":"ticket_id","in":"path","required":true,"schema":{"type":"string","title":"Ticket Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContribIntakeUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Intake V1 Admin Contrib Intake  Ticket Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/forecasts":{"get":{"tags":["forecasts"],"summary":"Forecasts","description":"Return day-ahead forecast snapshots across every forecast-capable\nISO adapter. Auth-free.\n\nThe 14 ISO adapters registered in ``src.data_feeds.iso.factory``\nare walked in deterministic (sorted) order. Each adapter that\noverrides ``fetch_hub_lmp_forecast`` contributes a snapshot;\nothers are silently skipped. Failures are surfaced via the\n``errors`` map so a transient ENTSO-E / JEPX outage doesn't\nblackhole the whole payload.\n\nCosts: one network fetch per forecast-capable adapter per call,\nbehind each adapter's own ``AdapterCache`` (30s TTL with 600s\nstale-fallback per the Lane-1 caching contract). Burst protection\ntherefore comes from the cache layer, not from this route.","operationId":"forecasts_v1_forecasts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Forecasts V1 Forecasts Get"}}}}}}},"/v1/forecasts/basis":{"get":{"tags":["forecasts-basis"],"summary":"Forecasts Basis","description":"Return per-hub forecast-vs-actual basis for a single ISO.\n\nTwo surfaces share this route:\n\n  * **Point-in-time** (default — no ``lookback_days``). Calls Lane 1's\n    ``forecast_vs_actual_basis(iso)`` and returns the helper payload\n    verbatim. Sign convention: ``basis_usd_per_mwh = actual − forecast``.\n    See the helper docstring for the full shape.\n\n  * **Historical aggregate** (``?lookback_days=N``). When Lane 1 R7\n    Part F lands, the helper grows a ``lookback_days`` kwarg and\n    the route forwards it. Until then, this route returns\n    ``{iso, lookback_days, aggregate: null, pending: ...}`` — a\n    well-formed envelope clients can branch on without 5xx-ing.\n    Capability probed at import time via inspect.signature.\n\nFailure modes (mapped from helper exceptions):\n\n  * ``KeyError`` (unknown ISO) → 404 with the ``supported_isos()``\n    list in the detail so callers can self-correct.\n  * ``LmpFetchError`` (either feed failed) → 503 with a short\n    reason string; the helper hard-fails on either-leg-missing.\n  * Any other unexpected exception → 500 with the type name and\n    a one-line reason. The caller sees a clean envelope; the\n    traceback lives in the dashboard logger.","operationId":"forecasts_basis_v1_forecasts_basis_get","parameters":[{"name":"iso","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":40,"description":"ISO code (e.g. 'caiso', 'miso', 'pjm').","title":"Iso"},"description":"ISO code (e.g. 'caiso', 'miso', 'pjm')."},{"name":"lookback_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":90,"minimum":1},{"type":"null"}],"description":"If provided, return the N-day historical aggregate of forecast-vs-actual basis instead of the point-in-time snapshot. Requires Lane 1 R7 Part F (historical aggregate helper); until that ships, the response includes `aggregate: null` + a `pending` note so callers can branch without 5xx-ing on capability.","title":"Lookback Days"},"description":"If provided, return the N-day historical aggregate of forecast-vs-actual basis instead of the point-in-time snapshot. Requires Lane 1 R7 Part F (historical aggregate helper); until that ships, the response includes `aggregate: null` + a `pending` note so callers can branch without 5xx-ing on capability."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Forecasts Basis V1 Forecasts Basis Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/forecasts/lmp-history":{"get":{"tags":["forecasts-lmp-history"],"summary":"Lmp History","description":"Return historical hub LMPs for one ISO over the last N days.\n\nFailure modes:\n  * Unknown ISO → handler returns count=0 (empty result set is\n    semantically correct, not a 404). A future tightening could\n    validate against ``supported_isos()`` and 404 explicitly;\n    for now treating \"no rows\" as a valid empty result lets\n    SDK callers branch on count without exception-handling.\n  * DB unavailable → 200 with kind='db_unavailable' so the SDK\n    can degrade rather than 5xx.","operationId":"lmp_history_v1_forecasts_lmp_history_get","parameters":[{"name":"iso","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":40,"description":"ISO code (caiso, miso, pjm, ...).","title":"Iso"},"description":"ISO code (caiso, miso, pjm, ...)."},{"name":"hub","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"description":"Optional hub filter (e.g. 'NP15'). When absent, returns rows across all hubs for the ISO.","title":"Hub"},"description":"Optional hub filter (e.g. 'NP15'). When absent, returns rows across all hubs for the ISO."},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Optional canonical region filter (us-east|us-west|us-central|eu-west|eu-central|uk|nordics|apac-north|apac-southeast|apac-anz|apac-greater-china|mena|latam|africa). Canonical 14-region taxonomy per region-taxonomy-v1.0.md; L1 maps ISO zones onto it (R13 Part B, migration 038).","title":"Region"},"description":"Optional canonical region filter (us-east|us-west|us-central|eu-west|eu-central|uk|nordics|apac-north|apac-southeast|apac-anz|apac-greater-china|mena|latam|africa). Canonical 14-region taxonomy per region-taxonomy-v1.0.md; L1 maps ISO zones onto it (R13 Part B, migration 038)."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Lookback window in days. Clamped to the 90d Neon retention window that the backfill maintains.","default":7,"title":"Days"},"description":"Lookback window in days. Clamped to the 90d Neon retention window that the backfill maintains."},{"name":"page","in":"query","required":false,"schema":{"type":"integer","maximum":100000,"minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Rows per page. Cap of 1000 matches the partner tier limits on other paginated endpoints.","default":100,"title":"Page Size"},"description":"Rows per page. Cap of 1000 matches the partner tier limits on other paginated endpoints."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Forward-only pagination cursor (base64-encoded ISO-8601 as_of). When supplied, returns rows strictly older than the cursor; ignores page. L1 R10 Part D. See docs/api/lmp-history-pagination.md.","title":"Cursor"},"description":"Forward-only pagination cursor (base64-encoded ISO-8601 as_of). When supplied, returns rows strictly older than the cursor; ignores page. L1 R10 Part D. See docs/api/lmp-history-pagination.md."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Lmp History V1 Forecasts Lmp History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/forecasts/accuracy":{"get":{"tags":["forecasts-accuracy"],"summary":"Forecasts Accuracy","description":"Return forecast-vs-actual accuracy metrics for one ISO.\n\n**R7.5 state — pending helper.** Returns a well-formed pending\nenvelope until Lane 1 ships the helper. Lane 5's defensive\nwrappers recognize ``status='pending_helper'`` and surface a\ncoverage-not-yet badge rather than a hard error.\n\n**Post-helper state — proxy.** Once Lane 1 ships\n``compute_forecast_accuracy(iso, days)``, this route forwards\nverbatim. The capability is probed at runtime so a future Lane 1\nship auto-flips this endpoint without an L4 redeploy.","operationId":"forecasts_accuracy_v1_forecasts_accuracy_get","parameters":[{"name":"iso","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":40,"description":"ISO code (caiso, miso, pjm, ...).","title":"Iso"},"description":"ISO code (caiso, miso, pjm, ...)."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Lookback window in days. Clamped to the 90d Neon retention window backfilled by Lane 1 migration 032.","default":7,"title":"Days"},"description":"Lookback window in days. Clamped to the 90d Neon retention window backfilled by Lane 1 migration 032."},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"description":"Optional canonical region filter (us-east|us-west|us-central|eu-west|eu-central|uk|nordics|apac-north|apac-southeast|apac-anz|apac-greater-china|mena|latam|africa). Canonical 14-region taxonomy per region-taxonomy-v1.0.md (R13 Part B). When set, restricts accuracy stats to rows whose iso_lmp_history.region matches.","title":"Region"},"description":"Optional canonical region filter (us-east|us-west|us-central|eu-west|eu-central|uk|nordics|apac-north|apac-southeast|apac-anz|apac-greater-china|mena|latam|africa). Canonical 14-region taxonomy per region-taxonomy-v1.0.md (R13 Part B). When set, restricts accuracy stats to rows whose iso_lmp_history.region matches."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Forecasts Accuracy V1 Forecasts Accuracy Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/exports/lender-pitch":{"post":{"tags":["exports"],"summary":"Lender Pitch","description":"Generate a one-page lender-pitch PDF and return the binary.\n\nInputs (all optional; query takes precedence over body):\n\n  query: ?partner_name=<str>&signer_name=<str>\n  body:  {\"partner_name\": \"Blue Owl\", \"signer_name\": \"Alex P.\",\n          \"sku\": \"H100\", \"region\": \"us_east\"}\n\nThe PDF (Lane 4 R7 Part D — lender pitch v2) ships with:\n  - Cover block + cover letter templated by partner_name\n  - Methodology surface table\n  - Settlement-grade SKU snapshot\n  - CLRI-DePIN basket block\n  - 5-year FLCI projection (illustrative)\n  - Signature block templated by signer_name\n\nResponse: ``application/pdf`` binary with a content-disposition\nsuitable for direct email attachment.","operationId":"lender_pitch_v1_exports_lender_pitch_post","parameters":[{"name":"partner_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Partner name templated into the cover letter + cover block. Query param takes precedence over body field of the same name — partner-ops scripts shell-pipe with curl, query is easier to splice.","title":"Partner Name"},"description":"Partner name templated into the cover letter + cover block. Query param takes precedence over body field of the same name — partner-ops scripts shell-pipe with curl, query is easier to splice."},{"name":"signer_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Name rendered in the signature block. Falls back to 'FLOPS Partner Operations' when absent. Query param takes precedence over body field of the same name.","title":"Signer Name"},"description":"Name rendered in the signature block. Falls back to 'FLOPS Partner Operations' when absent. Query param takes precedence over body field of the same name."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/security-events":{"get":{"summary":"List My Security Events","description":"Caller's own security events, DESC by occurred_at.\n\nFilters:\n  hours — rolling window, clamped to [1, 720]  (default 24)\n  limit — clamped to [1, 200]                  (default 50)\n\nNo event_type / severity filters here — the user-facing panel surfaces\neverything that touched their account so they can spot anomalies they\ndidn't initiate. The admin endpoint at /v1/admin/security-events has\nthe richer filter surface for ops use.","operationId":"list_my_security_events_v1_me_security_events_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List My Security Events V1 Me Security Events Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/passkey-status":{"get":{"summary":"My Passkey Status","description":"Lightweight status endpoint powering the /admin enrollment banner.\n\nReturns:\n  - ``has_passkey`` (bool): caller has at least one active passkey\n  - ``count`` (int): number of enrolled credentials\n  - ``session_factor`` (str | None): how this session was minted\n    (\"password\" or \"webauthn\" or None for env-operator / API key)\n  - ``scope`` (str): caller's scope (\"admin\" / \"staff\" / etc.) so the\n    frontend can decide whether to nag (admin = yes, others = no)\n\nUsed by:\n  - demo/static/admin.html banner that nags un-enrolled admins:\n    \"Enroll a passkey to access /admin from any IP\"\n  - future /account self-service passkey panel\n\nAuth: session or API key — anyone authenticated can ask about THEIR\nown status. Env-operator (no user_id) returns count=0 — they manage\npasskey enrollment per-user via the /v1/admin/users/<id>/webauthn/*\nflow, not for themselves.","operationId":"my_passkey_status_v1_me_passkey_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response My Passkey Status V1 Me Passkey Status Get"}}}}}}},"/v1/me/webauthn/status":{"get":{"summary":"Me Webauthn Status","description":"Return the caller's enrolled passkey credentials.\n\nShape:\n  {\n    \"user_id\": 42,\n    \"count\":   2,\n    \"credentials\": [\n      {\"credential_pk\": 7, \"label\": \"MacBook TouchID\",\n       \"created_at\": \"2026-05-19T...\", \"last_used_at\": \"...\",\n       \"sign_count\": 14},\n      ...\n    ]\n  }\n\nReturns ``count=0`` + ``kind=\"db_unavailable\"`` if the DB pool\ncan't be acquired — same fail-open posture as\n``/v1/me/passkey-status`` so the /account panel still renders.","operationId":"me_webauthn_status_v1_me_webauthn_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Me Webauthn Status V1 Me Webauthn Status Get"}}}}}}},"/v1/me/webauthn/register/start":{"post":{"summary":"Me Webauthn Register Start","description":"Issue PublicKeyCredentialCreationOptions for the caller.\n\nReturns:\n  JSONResponse({\"options\": <publickey-create options>, \"label\": str|None})\n  with a signed state cookie (flops_webauthn_state) carrying the\n  challenge + user_id. The browser feeds ``options`` to\n  ``navigator.credentials.create()``; the resulting attestation\n  response is POSTed to /v1/me/webauthn/register/finish with the\n  cookie attached automatically.","operationId":"me_webauthn_register_start_v1_me_webauthn_register_start_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RegisterStartRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webauthn/register/finish":{"post":{"summary":"Me Webauthn Register Finish","description":"Verify the attestation + persist the credential under the caller.\n\nReturns:\n  JSONResponse({\"credential_pk\": int, \"label\": str, \"note\": str})","operationId":"me_webauthn_register_finish_v1_me_webauthn_register_finish_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterFinishRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/webauthn/{credential_pk}":{"delete":{"summary":"Me Webauthn Revoke","description":"Revoke one of the caller's own passkeys (hard delete).\n\nMirrors the admin revoke endpoint but scoped to caller.user_id —\na user can ONLY delete their own credentials this way. Admins\ndeleting other users' credentials go through /v1/admin/users/<id>/\nwebauthn/<credential_pk>.","operationId":"me_webauthn_revoke_v1_me_webauthn__credential_pk__delete","parameters":[{"name":"credential_pk","in":"path","required":true,"schema":{"type":"integer","title":"Credential Pk"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Me Webauthn Revoke V1 Me Webauthn  Credential Pk  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/forensics/sla/rankings":{"get":{"tags":["forensics"],"summary":"Sla Rankings","description":"Per-provider SLA aggregate over the last ``window_hours``.\n\nReturns:\n  {\n    \"window_hours\": int,\n    \"generated_at\": ISO-8601,\n    \"count\":        int,                 # providers returned\n    \"ranked_count\": int,                 # subset with status='ranked'\n    \"providers\": [\n      {\"provider\": str, \"n_observations\": int,\n       \"avg_uptime_pct\": float|None, \"avg_p95_latency_ms\": float|None,\n       \"breach_count\": int, \"status\": \"ranked\"|\"insufficient_data\"},\n      ...\n    ],\n    \"kind\": \"user\" | \"db_unavailable\" | \"query_failed\"\n  }","operationId":"sla_rankings_v1_forensics_sla_rankings_get","parameters":[{"name":"window_hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"Lookback window in hours (default 24, max 720 / 30d).","default":24,"title":"Window Hours"},"description":"Lookback window in hours (default 24, max 720 / 30d)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sla Rankings V1 Forensics Sla Rankings Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/forensics/arbitrage/windows":{"get":{"tags":["forensics"],"summary":"Arbitrage Windows","description":"Per-provider arbitrage distribution over the last ``window_hours``.\n\nReturns:\n  {\n    \"window_hours\": int,\n    \"generated_at\": ISO-8601,\n    \"cadence_minutes\": int,\n    \"count\": int,\n    \"providers\": [\n      {\"provider\": str, \"undercut_minutes\": int,\n       \"overpriced_minutes\": int, \"avg_undercut_pct\": float|None,\n       \"avg_overprice_pct\": float|None,\n       \"n_undercut_observations\": int,\n       \"n_overpriced_observations\": int},\n      ...\n    ],\n    \"kind\": \"user\" | \"db_unavailable\" | \"query_failed\"\n  }\n\nSorted by undercut_minutes descending (most-aggressive providers\nfirst) so the dashboard panel surfaces the underwriting-risk\ncandidates at the top.","operationId":"arbitrage_windows_v1_forensics_arbitrage_windows_get","parameters":[{"name":"window_hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"default":24,"title":"Window Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Arbitrage Windows V1 Forensics Arbitrage Windows Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/totp/status":{"get":{"summary":"Totp Status","description":"Return caller's TOTP enrollment status.\n\nShape mirrors /v1/me/passkey-status — both surfaces nag from the\nsame banner; together they answer \"does this account have ANY 2FA\".","operationId":"totp_status_v1_me_totp_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Totp Status V1 Me Totp Status Get"}}}}}}},"/v1/me/totp/enroll-start":{"post":{"summary":"Totp Enroll Start","description":"Step 1: generate a fresh TOTP secret + return the otpauth URL.\n\nThe secret is NOT yet persisted to the DB — it's stashed in a\nsigned-cookie state token that the step-2 finish endpoint reads\nback. This prevents \"user scanned a bad QR but DB now thinks\nthey're enrolled\" silent lockouts.","operationId":"totp_enroll_start_v1_me_totp_enroll_start_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpEnrollStartRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/totp/enroll-finish":{"post":{"summary":"Totp Enroll Finish","description":"Step 2: verify the user's first code against the secret from the\nstate cookie. If it matches, persist + generate backup codes.","operationId":"totp_enroll_finish_v1_me_totp_enroll_finish_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TotpEnrollFinishRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Totp Enroll Finish V1 Me Totp Enroll Finish Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/totp":{"delete":{"summary":"Totp Revoke","description":"Revoke the caller's TOTP enrollment(s). Returns the count revoked.\n\nSets revoked_at on every non-revoked row for the user; doesn't hard-\ndelete so the audit trail (enrolled_at / last_used_at history) survives.","operationId":"totp_revoke_v1_me_totp_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Totp Revoke V1 Me Totp Delete"}}}}}}},"/v1/admin/sources":{"get":{"tags":["admin-sources"],"summary":"List Sources","operationId":"list_sources_v1_admin_sources_get","parameters":[{"name":"family","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"site_intel | pricing | data_feeds","title":"Family"},"description":"site_intel | pricing | data_feeds"},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"public_api | scraper | partner | csv_dump | rag","title":"Type"},"description":"public_api | scraper | partner | csv_dump | rag"},{"name":"cost","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"free | free_key | paid | partner_inbound","title":"Cost"},"description":"free | free_key | paid | partner_inbound"},{"name":"geography","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"substring match","title":"Geography"},"description":"substring match"},{"name":"sku","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"substring match (H100 / B300 / llama-3.1-70b / ...)","title":"Sku"},"description":"substring match (H100 / B300 / llama-3.1-70b / ...)"},{"name":"affects","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"substring match on affected tab/index","title":"Affects"},"description":"substring match on affected tab/index"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Sources V1 Admin Sources Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/sources/summary":{"get":{"tags":["admin-sources"],"summary":"Sources Summary","operationId":"sources_summary_v1_admin_sources_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sources Summary V1 Admin Sources Summary Get"}}}}}}},"/v1/admin/sources/health":{"get":{"tags":["admin-sources"],"summary":"Sources Health","description":"Down-detector — green/amber/red/never counts and red list.","operationId":"sources_health_v1_admin_sources_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sources Health V1 Admin Sources Health Get"}}}}}}},"/v1/admin/sources/{source_id}":{"get":{"tags":["admin-sources"],"summary":"Get Source Detail","operationId":"get_source_detail_v1_admin_sources__source_id__get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Source Detail V1 Admin Sources  Source Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/refdata/gpu-capex":{"get":{"tags":["refdata"],"summary":"Get Gpu Capex Seed","description":"Full seed map. Partners can fetch and cache locally.","operationId":"get_gpu_capex_seed_v1_refdata_gpu_capex_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Gpu Capex Seed V1 Refdata Gpu Capex Get"}}}}}}},"/v1/refdata/gpu-capex/{sku}":{"get":{"tags":["refdata"],"summary":"Get Gpu Capex One","description":"Single SKU. 404 if absent.","operationId":"get_gpu_capex_one_v1_refdata_gpu_capex__sku__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Gpu Capex One V1 Refdata Gpu Capex  Sku  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/refdata/gpu-capex/{sku}/observations":{"get":{"tags":["refdata"],"summary":"Get Gpu Capex Observations","description":"Audit-trail surface — return the raw gpu_capex_observations rows that\nfeed the LIVE-tier cascade for this SKU.\n\nSatisfies IOSCO Principle 17 (auditable inputs) by giving partners +\nauditors a way to drill from a published LIVE value back to the\nunderlying observations. Rows ordered observed_at DESC; default\nlimit 50, clamped to [1, 500].\n\nReturns 503 if the DB pool is unavailable OR the observations table\ndoesn't exist yet (migration 012 not applied). Soft-fails to an\nempty list with a note if the SKU has zero observations — never\na 404, so callers can poll cleanly as the collector populates rows.\n\nResponse shape:\n  {\n    \"sku\": str,\n    \"count\": int,\n    \"limit\": int,\n    \"rows\": [\n      {observed_at, source, price_usd, condition, item_id, payload}\n    ],\n    \"by_source\": {source: count},\n    \"methodology\": \"https://app.flopsindex.com/methodology/GPU_CAPEX_LIVE_METHODOLOGY.md\"\n  }","operationId":"get_gpu_capex_observations_v1_refdata_gpu_capex__sku__observations_get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Gpu Capex Observations V1 Refdata Gpu Capex  Sku  Observations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/refdata/gpu-capex/{sku}/secondary":{"get":{"tags":["refdata"],"summary":"Get Gpu Capex Secondary","description":"Secondary-market signal for an SKU. eBay completed-listings bounds\n(committed reference, not live scrape) + roadmap pointer for the\npersisted-collector LIVE tier.\n\nReturns 404 if the SKU has no eBay-adapter coverage yet (most\nnon-Nvidia SKUs aren't templated). Use this endpoint when you want\nprocurement-timing signal without conflating it with the primary\nseed price.","operationId":"get_gpu_capex_secondary_v1_refdata_gpu_capex__sku__secondary_get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Gpu Capex Secondary V1 Refdata Gpu Capex  Sku  Secondary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/refdata-jobs/gpu-capex/observations/collect":{"post":{"tags":["refdata-jobs-admin"],"summary":"Collect Gpu Capex Observations Endpoint","description":"Manual fire of the gpu_capex_observations daily collector.\n\nAdmin-scope only. Returns per-SKU insertion counts. Idempotent —\nre-running the same day adds zero new rows because eBay item_ids\nare stable. Useful for one-shot population after migration 012\napplies + for post-deploy smoke. Operator-targeted; the production\ncadence belongs on a Fly scheduled machine (deferred).\n\nReachable at POST /v1/refdata-jobs/gpu-capex/observations/collect\n(NOT under /v1/refdata/ — that path is allow-prefixed for public\nreads, which bypasses auth and breaks the admin guard here).","operationId":"collect_gpu_capex_observations_endpoint_v1_refdata_jobs_gpu_capex_observations_collect_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Collect Gpu Capex Observations Endpoint V1 Refdata Jobs Gpu Capex Observations Collect Post"}}}}}}},"/v1/price/graduations":{"get":{"tags":["public"],"summary":"Public Graduations","description":"Recent regional k-anon tier crossings. Auth-free, read-only.\n\nOne row per (slug, region) per tick whose regional_data_tier changed:\na `graduation` (below_k_anon→LIVE) or a `regression` (LIVE→below_k_anon).\nSource: region_graduation_events (L3 graduation_monitor).\n\nPer r14-contracts-v1.0 Decision 2 + the R13 no-source-count policy, the raw\n`num_providers` count is OMITTED on the public, auth-free response — only the\ntier LABELS (from_tier → to_tier), which are safe. An authenticated caller\n(valid X-API-Key OR logged-in dashboard session) additionally gets\n`num_providers` per event, in parity with the auth-gated tier.","operationId":"public_graduations_v1_price_graduations_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"graduation | regression | all","default":"all","title":"Direction"},"description":"graduation | regression | all"},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to one canonical region","title":"Region"},"description":"Filter to one canonical region"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Graduations V1 Price Graduations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/price/{slug}":{"get":{"tags":["public"],"summary":"Public Price","description":"Canonical one-fact envelope for a FLOPS index. Auth-free.\n\nResponse shape is stable. Additions allowed; field removals are\nbreaking. Carries NO methodology, NO source counts, NO per-source\nattribution by design.\n\nOptional regional decomposition (Lane 4 R9 Part C): pass ``region``\nto get the per-region value sourced from index_values_regional;\npass ``basis=true`` to also get the regional - global delta.","operationId":"public_price_v1_price__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"region","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Canonical region code (us-east, eu-west, ...). Omit for global. See methodology/flops/regional_decomposition_v1.0.md.","title":"Region"},"description":"Canonical region code (us-east, eu-west, ...). Omit for global. See methodology/flops/regional_decomposition_v1.0.md."},{"name":"basis","in":"query","required":false,"schema":{"type":"boolean","description":"When true + region set, response carries the (regional - global) basis as a separate field.","default":false,"title":"Basis"},"description":"When true + region set, response carries the (regional - global) basis as a separate field."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Price V1 Price  Slug  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/price/{slug}.txt":{"get":{"tags":["public"],"summary":"Public Price Txt","description":"Plain-text one-liner. Agent-pasteable into chat responses.","operationId":"public_price_txt_v1_price__slug__txt_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/price/spread/regional":{"get":{"tags":["public"],"summary":"Public Regional Spread","description":"Catalog-wide regional spread, one row per public spot index. Auth-free.\n\nThe trader-facing keystone: for every public SKU with ≥2 priced regions,\nreturns the max-region / min-region / spread / spread_pct. Sorted widest\nspread first. The headline FLOPS-H100-OD row reads us-east vs eu-west =\n~$2.17/GPU-hr.\n\nR15 §C4: anonymous → ~1h-delayed + rounded; authed → real-time + full\nprecision (private/no-store). Reads index_values_regional (shared with L3's\npublisher + the MCP get_regional_spread tool). Fails OPEN.","operationId":"public_regional_spread_v1_price_spread_regional_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Public Regional Spread V1 Price Spread Regional Get"}}}}}}},"/v1/price/{slug}/regional":{"get":{"tags":["public"],"summary":"Public Price Regional","description":"Per-region decomposition for one index across all canonical regions.\n\nAuth-free. Returns the global value (context) + one entry per region the\npublisher has written, in canonical display order, each carrying its\nvalue + data_tier label (LIVE / below_k_anon / insufficient_sources).\nAlso returns the computed spread so a single fetch backs both the F4\nregion-chip row and the per-SKU spread headline.\n\nR15 §C4: anonymous → ~1h-delayed + rounded; authed → real-time + full\nprecision (private/no-store). Fails OPEN.","operationId":"public_price_regional_v1_price__slug__regional_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Price Regional V1 Price  Slug  Regional Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/price/{slug}/spread/history":{"get":{"tags":["public"],"summary":"Public Spread History","description":"Regional spread (max-region − min-region) over time for one index.\n\nAuth-free. The trader's \"is the basis widening or mean-reverting?\" view —\nthe time dimension on top of /v1/price/spread/regional. Points carry the\nspread + the max/min region pair so the UI can flag a pair flip.\n\n``tier_floor=LIVE`` recomputes each tick's spread over settlement-grade\n(LIVE) regions only — the F4 \"settlement-grade only\" toggle. Default ALL\nkeeps continuity with the snapshot headline (r14-contracts Decision 1).","operationId":"public_spread_history_v1_price__slug__spread_history_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","default":"30d","title":"Range"}},{"name":"tier_floor","in":"query","required":false,"schema":{"type":"string","description":"ALL = spread over all priced regions (default, matches the snapshot headline). LIVE = settlement-grade only (per-tick, only regions LIVE at that tick contribute). r14-contracts D1.","default":"ALL","title":"Tier Floor"},"description":"ALL = spread over all priced regions (default, matches the snapshot headline). LIVE = settlement-grade only (per-tick, only regions LIVE at that tick contribute). r14-contracts D1."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Spread History V1 Price  Slug  Spread History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/inference/qos":{"get":{"tags":["public"],"summary":"Public Inference Qos","description":"Inference Quality-of-Service scores, one row per (provider, model).\n\nAuth-free, citable. Each target carries the 0-100 composite SCORE plus the\nfive measured sub-metrics (ttft_ms / throughput_tps / p95_ms / errorrate_pct\n/ uptime_pct). Read alongside /v1/price/{model}-INFERENCE_OUTPUT for the\nprice×quality view. PREVIEW tier — informational, not settlement-eligible.\n\nFails OPEN: an empty result (no rows / dark pool / pre-migration) returns\na 200 with an empty models list + note, never a 503.","operationId":"public_inference_qos_v1_inference_qos_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"default":48,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Inference Qos V1 Inference Qos Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/ts/{slug}":{"get":{"tags":["public"],"summary":"Public Timeseries","description":"Public decimated timeseries. ≤200 points per call. Auth-free.\n\nCarries only [ts, value] tuples — no per-tick num_sources or\nmethodology. Decimation is uniform-stride so the agent can plot\na reasonable line without holding the full history.\n\nR15 §C4: anonymous → series trimmed to <= now-delay + rounded; authed →\nreal-time + full precision (private/no-store).","operationId":"public_timeseries_v1_ts__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"range","in":"query","required":false,"schema":{"type":"string","default":"7d","title":"Range"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Timeseries V1 Ts  Slug  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/search":{"get":{"tags":["public"],"summary":"Public Search","description":"Resolve a free-text query to canonical index slugs. Auth-free.\n\nPure substring + token scoring against the public catalog. We do\nNOT expose ranking weights or scoring internals (would leak the\ncatalog structure); just return ordered slugs + descriptions.","operationId":"public_search_v1_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Public Search V1 Search Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/methodology":{"get":{"summary":"List Methodologies","description":"List every published methodology with its active version.","operationId":"list_methodologies_v1_methodology_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Methodologies V1 Methodology Get"}}}}}}},"/v1/methodology/{slug}":{"get":{"summary":"Methodology Default","description":"Alias for /v1/methodology/{slug}/versions.","operationId":"methodology_default_v1_methodology__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Methodology Default V1 Methodology  Slug  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/methodology/{slug}/versions":{"get":{"summary":"Methodology Versions","description":"Full version history for one methodology_id.","operationId":"methodology_versions_v1_methodology__slug__versions_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Methodology Versions V1 Methodology  Slug  Versions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/methodology/{slug}/{version}":{"get":{"summary":"Methodology Doc Md","description":"Raw markdown for one published methodology version.","operationId":"methodology_doc_md_v1_methodology__slug___version__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"version","in":"path","required":true,"schema":{"type":"string","title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/methodology/{slug}/{version}.json":{"get":{"summary":"Methodology Doc Json","description":"JSON wrapper: frontmatter parsed, body included as one string.","operationId":"methodology_doc_json_v1_methodology__slug___version__json_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"version","in":"path","required":true,"schema":{"type":"string","title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/docs/index":{"get":{"summary":"Docs Index","description":"JSON tree of every available doc. Section-grouped, stable order.\n\nShape:\n  {\n    \"methodology\": [{slug, family, title, url}, ...],\n    \"rest\":        [{slug, title, url}, ...],\n    \"mcp\":         [{slug, title, url}, ...],\n    \"count\": int\n  }","operationId":"docs_index_v1_docs_index_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Docs Index V1 Docs Index Get"}}}}}}},"/v1/docs/{slug}":{"get":{"summary":"Docs One","description":"Return raw markdown for one doc.\n\nReturns 404 (not 500) on unknown slugs so a partner who guessed at\na URL gets a clean \"no such doc\" rather than an internal error.","operationId":"docs_one_v1_docs__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/keys/sub":{"get":{"summary":"List My Sub Keys","description":"List the caller's active sub-keys. Per-user callers see only\nsub-keys minted by their user-key; org-wide callers see only\nsub-keys minted by their org-wide key (never the user-owned ones —\nthose are private to each user under the same org).","operationId":"list_my_sub_keys_v1_me_keys_sub_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Mint Sub Key","description":"Mint a short-lived sub-key under the caller's parent.\n\nBody (optional JSON):\n  {\n    \"label\": \"agent-run-7\",        # free-form, ≤255 chars\n    \"ttl_hours\": 24,               # 1..168 (cap 7d); default 24\n    \"concurrency_max\": 5,          # 1..50; default 5\n    \"run_id\": \"abc-123\"            # optional X-Caller-Run-Id seed\n  }\n\nResponse:\n  {\n    \"sub_key\": \"flops_sub_XXXXXXX...\",  # SHOWN ONCE\n    \"id\": 42,\n    \"key_prefix\": \"flops_sub_XXXXX\",\n    \"expires_at\": \"2026-05-19T...\",\n    \"concurrency_max\": 5,\n    \"label\": \"agent-run-7\"\n  }","operationId":"mint_sub_key_v1_me_keys_sub_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/me/keys/sub/{sub_key_id}":{"delete":{"summary":"Revoke My Sub Key","description":"Revoke a sub-key. Scope is enforced by parent_user_id (user\ncallers) OR parent_org_id (org-wide callers) — never both — so\ncross-tenant revoke attempts return 404 with no signal.","operationId":"revoke_my_sub_key_v1_me_keys_sub__sub_key_id__delete","parameters":[{"name":"sub_key_id","in":"path","required":true,"schema":{"type":"integer","title":"Sub Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/me/keys/sub/{sub_key_id}/usage":{"get":{"summary":"Sub Key Usage","description":"Return a usage rollup for one sub-key, scoped to the caller's\nparent identity. Closes the \"what did my agent actually do with the\nsub-key I minted?\" gap.\n\nFilters:\n  hours   1..720 (cap 30d). Default 168 (7d).\n\nResponse shape:\n  {\n    \"sub_key_id\": int,\n    \"label\": str|null,\n    \"window_hours\": int,\n    \"totals\": {hits, errors, p50_ms, p95_ms, unique_runs, unique_paths},\n    \"by_path\":  [{path, hits, errors, p95_ms}, ...]   top 15\n    \"by_status\":[{status_code, hits}, ...]\n    \"by_run\":   [{caller_run_id, hits, last_seen}, ...] top 10\n    \"recent\":   [{occurred_at, method, path, status, latency_ms,\n                  caller_run_id, caller_subagent_id}, ...] last 50\n  }\n\nOwnership: the sub-key must have been minted by the caller (per-user\ncallers see only their own; org-wide service-key callers see only\nsub-keys minted by the org-wide parent — same scope rules as the\nlist endpoint).","operationId":"sub_key_usage_v1_me_keys_sub__sub_key_id__usage_get","parameters":[{"name":"sub_key_id","in":"path","required":true,"schema":{"type":"integer","title":"Sub Key Id"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":168,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/dashboard":{"get":{"summary":"Dashboard","description":"Serve the terminal UI at a friendly path.\n\nSets no-cache so future deploys are immediately visible — without\nthis, browsers heuristically cache the HTML for hours and JS/HTML\nfixes appear \"stuck\" on the user side.","operationId":"dashboard_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/admin":{"get":{"summary":"Admin Page","description":"Server-rendered admin UI for partner/user management + activity\naudit. Gated on scope='admin' — non-admin partners get a 403 HTML\npage (not the basic-auth challenge, which would already have fired\nin middleware for any auth'd request that got this far).\n\nThe HTML page itself just makes browser-side fetches to the\n/v1/admin/* REST endpoints which have their own _require_admin\ngate; this front-gate is purely UX so non-admin users don't see\na blank loading skeleton.","operationId":"admin_page_admin_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/account":{"get":{"summary":"Account Page","description":"Partner-facing self-service: see + revoke devices, manage agent\nsub-keys. Behind BasicAuthMiddleware — any authed partner-tier\ncaller can land here. No admin scope required; users see only\ntheir own rows via the underlying /v1/sessions/me + /v1/me/keys/sub\nendpoints which scope by request.state.partner.user_id internally.","operationId":"account_page_account_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/verify":{"get":{"summary":"Verify Index Value","description":"Verify a cited FLOPS index value against the canonical source.\n\nPattern the SDK + LLM agents are expected to call:\n    value = flopsindex.get('FLOPS-H100-OD')\n    assert flopsindex.verify('FLOPS-H100-OD', value=value).verified\n\nReturns {verified, actual_value, delta_pct, ts, source_url}. If `ts`\nis omitted, verifies against the latest official close. If `expected`\nis omitted, just returns the actual value with verified=null (for\n\"what's the canonical value at ts X\").\n\nThis is the keystone Track D mechanic — once agents are calling\n/verify, FLOPS becomes the canon they verify against (not a source\nthey compete with). Telemetry (which (index, ts, value) tuples are\nbeing verified) becomes near-real-time signal of LLM citation\npatterns + hallucination rate per index.\n\nNOTE: v0 stub — uses the in-process manifest values. Phase 4 keystone\ncontract makes this signed + audit-traceable per the inputs-receipt\ntable; this stub stays compatible.","operationId":"verify_index_value_v1_verify_get","parameters":[{"name":"index_id","in":"query","required":true,"schema":{"type":"string","title":"Index Id"}},{"name":"ts","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ts"}},{"name":"expected","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Expected"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Verify Index Value V1 Verify Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/methodology/{name}":{"get":{"summary":"Methodology Doc","description":"Serve methodology spec docs from Flops_Unicron/docs/methodology/.\n\nLinked from F13 and referenced by /v1/quality/methodology. Path\ntraversal blocked: only files inside the methodology directory are\nreachable.","operationId":"methodology_doc_methodology__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","description":"Send humans to the dashboard. API consumers can hit /api for the JSON index.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/catalog":{"get":{"summary":"V1 Catalog","description":"List every public FLOPS index with its current value + provenance.\n\nStable JSON envelope — additions allowed, no breaking changes within v1.\nPartner SDKs paginate via the upstream list ordering (alphabetical by\nindex_id) — no cursor for v1; full catalog is < 200 entries.\n\nPer-family `as_of` / `num_sources` / `data_tier` are surfaced from the\nnested `meta` block when not at the top level (FLOPS-* and grid\nfamilies nest them; FLCI/SMPI/CLRI/GRVCI carry timestamps directly\non the redis blob via `_extract_ts_from_row`). Catalog audit\n2026-05-17 found EVERY index had `as_of=null` because the v1\ncatalog only read top-level keys — fix surfaces freshness so\npartners can detect staleness on the catalog hit alone.","operationId":"v1_catalog_v1_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response V1 Catalog V1 Catalog Get"}}}}}}},"/v1/indices/{index_id}/current":{"get":{"summary":"V1 Index Current","description":"Latest published tick for one index. Returns the canonical value +\nage + a citation hint pointing to /v1/audit/receipt/* for the signed\nreceipt covering this tick.\n\n404 when the index is unknown (vs returning {value: null}) so partner\ncode can distinguish 'index does not exist' from 'index exists but\nis currently dark'.","operationId":"v1_index_current_v1_indices__index_id__current_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response V1 Index Current V1 Indices  Index Id  Current Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/indices/{index_id}/history":{"get":{"summary":"V1 Index History","description":"Recent published ticks for one index. ``limit`` capped at 720\n(30 days of hourly data) to bound response size; partners can\npaginate by calling with smaller limits + filtering client-side.","operationId":"v1_index_history_v1_indices__index_id__history_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response V1 Index History V1 Indices  Index Id  History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api":{"get":{"summary":"Api Index","description":"Service-info / endpoint catalog for API consumers.","operationId":"api_index_api_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Api Index Api Get"}}}}}}},"/demo/manifest":{"get":{"summary":"Manifest","description":"Seeded state — indices published, operator hashes, cohort sizes.\n\nCopy an operator_hash from here into the /benchmarking endpoint.\nTry one from each cohort to see the different UX paths.","operationId":"manifest_demo_manifest_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Manifest Demo Manifest Get"}}}}}}},"/demo/live/refresh":{"post":{"summary":"Live Refresh","description":"Fetch live data from public provider endpoints and publish CLRI spot.\n\nWrites CLRI-{display}-Spot for every SKU where we have coverage.\nReturns a per-collector status + per-SKU aggregated summary so the\nUI can show provenance (which providers contributed, how many points,\nMAD-trim dropouts).","operationId":"live_refresh_demo_live_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Live Refresh Demo Live Refresh Post"}}}}}}},"/demo/live/status":{"get":{"summary":"Live Status","description":"Return the last live-refresh result without re-fetching.","operationId":"live_status_demo_live_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Live Status Demo Live Status Get"}}}}}}},"/demo/live/lmp/refresh":{"post":{"summary":"Live Lmp Refresh","description":"Pull real-time LMP from configured ISOs, cache per-zone results.\n\nReturns per-region snapshot with source attribution, freshness, hub\nbreakdown, and fallback signals so the UI can render a credible\nprovenance chip.\n\nPer-ISO behaviour:\n- ERCOT: always live (public MIS, no auth)\n- PJM:   live when PJM_API_KEY env var is set; otherwise reports\n         \"not configured\" with the reason so the UI can prompt.","operationId":"live_lmp_refresh_demo_live_lmp_refresh_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Live Lmp Refresh Demo Live Lmp Refresh Post"}}}}}}},"/demo/live/lmp":{"get":{"summary":"Live Lmp Status","description":"Return the last live-LMP result without re-fetching.","operationId":"live_lmp_status_demo_live_lmp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Live Lmp Status Demo Live Lmp Get"}}}}}}},"/demo/sei":{"get":{"summary":"Sei All","description":"All SEI (Scheduling Efficiency Index) values grouped by family.","operationId":"sei_all_demo_sei_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sei All Demo Sei Get"}}}}}}},"/demo/eu-power-prices":{"get":{"summary":"Eu Power Prices","description":"ENTSO-E day-ahead clearing prices (EUR/MWh) for major EU bidding\nzones. Cached 1h in-process. Surfaced on F5 GRID alongside US LMP\nso finance users can compare US vs EU compute power costs.\n\nSource: https://transparency.entsoe.eu/ (documentType=A44).\nToken: ENTSOE_API_TOKEN secret on flops-dashboard.","operationId":"eu_power_prices_demo_eu_power_prices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Eu Power Prices Demo Eu Power Prices Get"}}}}}}},"/demo/grid-indices":{"get":{"summary":"Grid Indices","description":"All published grid + efficiency indices — PAI, TFLI, CFEI-I, CP5.\n\nDemo surface for v6.4 index families that don't carry a contributor\ncoverage_tier (PAI/TFLI/CFEI-I are public-data; CP5 is settlement-data\nfrom PJM). Production consumers should use the SpotPriceBuildv0 API\nat /v1/indices/{code} which returns live calculator-orchestrator output.\n\nEvery entry includes data_tier so callers cannot mistake an Indicative\nvalue for Reference-grade. proxy_inputs is surfaced top-level (not just\nin metadata) for the same reason.","operationId":"grid_indices_demo_grid_indices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Grid Indices Demo Grid Indices Get"}}}}}}},"/demo/all-indices":{"get":{"summary":"All Indices","description":"Every published index in the demo — for the Browser tab.\n\nReturns a flat list with {family, index_id, key_value_or_summary, meta}\nsuitable for client-side filtering/sorting.","operationId":"all_indices_demo_all_indices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response All Indices Demo All Indices Get"}}}}}}},"/demo/index/{index_id}/latest":{"get":{"summary":"Demo Index Latest","description":"Per-index latest tick — dashboard-internal mirror of the v2\ncatalog ``/v2/indices/{id}/current`` endpoint.\n\nPowers F15 Reference tab's detail panel: standard-catalog indices\n(FLCI/SMPI/CLRI/GRVCI/SEI/PAI/TFLI/CFEI/FLOPS) read from the same\nRedis manager + Neon path /demo/all-indices uses, so F15 doesn't\nhave to carry an API key for v2 access just to display a price.\n\nPer-family value field varies (value_usd_per_gpu_hr / median_price_usd /\nvalue / lambda_p50); the response normalises to a single ``value``\nfield with the family-correct unit. ``timestamp`` is the\n``ran_at`` / ``timestamp`` /``as_of`` carried on the redis blob\nwhen present, else null.","operationId":"demo_index_latest_demo_index__index_id__latest_get","parameters":[{"name":"index_id","in":"path","required":true,"schema":{"type":"string","title":"Index Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Demo Index Latest Demo Index  Index Id  Latest Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/flops-pricing":{"get":{"summary":"Flops Pricing Indices","description":"Today's FLOPS-* pricing indices from Neon (collector-driven hourly).\n\nReturns ``{\"indices\": [...], \"count\": N, \"as_of\": \"YYYY-MM-DD\"}`` where\neach row is shaped for the Index Browser. Each row carries BOTH:\n  - meta.daily_value (= index_values.value)  — daily rollup\n  - meta.latest_oracle (= oracle_prices.median_price) — current hourly tick\n    which is what /v1/price/{slug} serves to agents.\nSurfacing both inline so the F4 panel can show the spread without\nneeding N+1 fetches against /demo/oracle-proof.","operationId":"flops_pricing_indices_demo_flops_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Flops Pricing Indices Demo Flops Pricing Get"}}}}}}},"/demo/b300-observations":{"get":{"summary":"B300 Observations","description":"Latest B300 observations from raw_pricing — backs the F14 BLACKWELL\nprovider-contributions table. One row per (provider, region) in the\nmost recent collector cycle, with the indicative-tier flag exposed so\nthe UI can mark static-supplemented rows.","operationId":"b300_observations_demo_b300_observations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{},"type":"array","title":"Response B300 Observations Demo B300 Observations Get"}}}}}}},"/demo/sku-coverage":{"get":{"summary":"Sku Coverage","description":"Coverage status per canonical SKU — backs the colored chip UI in\nthe F14 CUSTOM INDEX builder. Returns one row per canonical SKU with\nprovider count, observation count, MAD/median variance ratio, and\na traffic-light status (green/yellow/red) computed against k-anon +\nvariance thresholds.\n\nStatus rules (k-anon = 3 floor):\n  green : providers >= 3 AND mad_over_median <  0.50\n  yellow: providers == 2 OR  mad_over_median in [0.50, 1.00]\n  red   : providers <= 1 OR  mad_over_median >  1.00 OR no data","operationId":"sku_coverage_demo_sku_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Sku Coverage Demo Sku Coverage Get"}}}}}}},"/demo/region-coverage":{"post":{"summary":"Region Coverage","description":"Region coverage for a given SKU set. Frontend posts the SKU list,\nreceives raw provider_region buckets with provider lists + counts.\nThe frontend bins these into macro region groups (Nordics, US-WTX,\nAPAC NE, etc.) using the same substring patterns the index calculator\napplies — keeping macro labels client-side avoids a second source of\ntruth for region taxonomy.","operationId":"region_coverage_demo_region_coverage_post","requestBody":{"content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Body"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Region Coverage Demo Region Coverage Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/oracle-prices":{"get":{"summary":"Oracle Prices Latest","description":"Latest oracle_prices snapshot — settlement-grade aggregations with proof.\n\nOne row per (gpu_model, index_type) at the most recent oracle run.","operationId":"oracle_prices_latest_demo_oracle_prices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Oracle Prices Latest Demo Oracle Prices Get"}}}}}}},"/demo/oracle-proof/{gpu_model}/{index_type}":{"get":{"summary":"Oracle Proof","description":"Latest oracle_prices row for (gpu_model, index_type) + 24-row history.\n\nReturns the SHA-256 observations_hash and parsed computation_proof JSON\n— settlement-grade audit surface. ``{}`` when no rows match.","operationId":"oracle_proof_demo_oracle_proof__gpu_model___index_type__get","parameters":[{"name":"gpu_model","in":"path","required":true,"schema":{"type":"string","title":"Gpu Model"}},{"name":"index_type","in":"path","required":true,"schema":{"type":"string","title":"Index Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Oracle Proof Demo Oracle Proof  Gpu Model   Index Type  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/itpi-snapshot":{"get":{"summary":"Itpi Snapshot","description":"ITPI (Inference Token Price Index) snapshot for the F4 panel.\n\nOne row per model with paired input + output $/1M-token prices,\nnum_sources per direction, freshness age. Reads oracle_prices for\nindex_type in (inference_input, inference_output) — commercial-\neligible via the provider-direct rebuild (NOT the restricted aa:*\nAA path).","operationId":"itpi_snapshot_demo_itpi_snapshot_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":24,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Itpi Snapshot Demo Itpi Snapshot Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/iqos-snapshot":{"get":{"summary":"Iqos Snapshot","description":"IQOS (Inference Quality-of-Service) snapshot for the F4 price×quality panel.\n\nOne row per (provider, model) with the composite 0-100 quality SCORE, the\nfive measured sub-metrics (TTFT / throughput / p95 / errorrate / uptime),\nand the model's paired ITPI input/output token price. Reads oracle_prices\nfor the IQOS family written by SpotPriceBuildv0's inference-QoS publisher.\nIQOS v0.1 is preview-tier (informational, not settlement-eligible).","operationId":"iqos_snapshot_demo_iqos_snapshot_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":48,"title":"Hours"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Iqos Snapshot Demo Iqos Snapshot Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/provider-region-prices":{"get":{"summary":"Provider Region Prices","description":"Per-(provider, sub-region) price detail for the F4 Catalog drill-down.\n\nThe macro-region FLCI grid (us_east / us_west / eu_west / uk / me)\nrolls up 25+ raw_pricing sub-regions. This endpoint exposes the\nunderlying sub-region prices so finance users can read regional\narbitrage signal (e.g. azure_retail/eastus vs westus3 vs uksouth)\nwithout leaving the dashboard.\n\nReturns flat rows + summary stats (min/median/max/p25/p75).","operationId":"provider_region_prices_demo_provider_region_prices_get","parameters":[{"name":"gpu_model","in":"query","required":true,"schema":{"type":"string","title":"Gpu Model"}},{"name":"pricing_model","in":"query","required":false,"schema":{"type":"string","default":"on_demand","title":"Pricing Model"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","default":2,"title":"Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Provider Region Prices Demo Provider Region Prices Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/sku/{sku}":{"get":{"summary":"Sku Drilldown","description":"All indices published for a given SKU — FLCI matrix + SMPI tiers + CLRI curve + GRVCI.","operationId":"sku_drilldown_demo_sku__sku__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Sku Drilldown Demo Sku  Sku  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/forward-curve/{sku}":{"get":{"summary":"Forward Curve","description":"CLRI term structure for one SKU — Spot through 36mo where published.\n\nDrives the forward-curve chart on the Procurement tab. Curve shape\n(contango = rising, backwardation = falling) is the TradFi-audience\nread of how the market is pricing duration risk.","operationId":"forward_curve_demo_forward_curve__sku__get","parameters":[{"name":"sku","in":"path","required":true,"schema":{"type":"string","title":"Sku"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Forward Curve Demo Forward Curve  Sku  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/regional":{"get":{"summary":"Regional","description":"Regional snapshot — FLCI per (sku, region) at the requested workload/phase.\n\nDrives the MAP tab. Uses the seeded regions (us_east, us_west, eu_west,\nuk, me) plus a 'global' aggregate when present.\n\nAlso returns a ``live`` block per SKU when fresh oracle_prices exist,\noverlaying live market reference prices on top of the seed FLCI grid.\nFrontend renders this as a \"LIVE\" badge on the region cards with\nage-based freshness color.","operationId":"regional_demo_regional_get","parameters":[{"name":"workload","in":"query","required":false,"schema":{"type":"string","default":"OpenSource","title":"Workload"}},{"name":"phase","in":"query","required":false,"schema":{"type":"string","default":"Training","title":"Phase"}},{"name":"live_index_type","in":"query","required":false,"schema":{"type":"string","default":"on_demand","title":"Live Index Type"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Regional Demo Regional Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/docs-md/{slug}":{"get":{"summary":"Methodology Doc","description":"Serve methodology markdown files rendered as minimal HTML.\n\nLets the dashboard link from a panel to its methodology ('METHODOLOGY ↗'\nbuttons). Slugs map 1:1 to files under docs/methodology/.","operationId":"methodology_doc_docs_md__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/basis":{"get":{"summary":"Power Compute Basis","description":"Power-Compute Basis — FLCI minus raw power cost, per region.\n\n``basis = FLCI_$/GPU-hr − (TDP_kW × LMP_$/MWh / 1000 × PUE × PSU_overhead)``\n\nThis is the margin layer — everything FLCI prices ABOVE the raw wholesale\npower cost of running that GPU at the local ISO rate. It's the thing a\nderivatives desk would actually price: FLCI spread vs LMP, zone-resolved.\n\nRegions map to LMP zones via ``demo.map_data.LMP_ZONES`` (first zone per\nregion by priority — PJM for us_east, ERCOT for us_west, etc.).","operationId":"power_compute_basis_demo_basis_get","parameters":[{"name":"sku","in":"query","required":false,"schema":{"type":"string","default":"h100_sxm5","title":"Sku"}},{"name":"workload","in":"query","required":false,"schema":{"type":"string","default":"OpenSource","title":"Workload"}},{"name":"phase","in":"query","required":false,"schema":{"type":"string","default":"Training","title":"Phase"}},{"name":"pue","in":"query","required":false,"schema":{"type":"number","default":1.25,"title":"Pue"}},{"name":"use_live_lmp","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Use Live Lmp"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Power Compute Basis Demo Basis Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/map/datacenters":{"get":{"summary":"Map Datacenters","description":"GeoJSON FeatureCollection of datacenter locations across all tiers.\n\n78 facilities across us_east, us_west, uk, eu_west, me, tagged by\ncoverage tier:\n  tier=1  Indexed — operator contributes to FLCI/CLRI\n  tier=2  Observable — public pricing via marketplaces (Vast/Lambda/RunPod/etc.)\n  tier=3  Known, Unobserved — facility exists, no data contribution yet\n\nEach feature also carries:\n  observed_skus     — list of dashboard SKU ids observed in Neon for the\n                      (operator, region) of this DC, last 10-min cache window\n  primary_sku       — first observed SKU when live, else seed primary_sku\n  seed_primary_sku  — original hardcoded primary_sku for fallback / audit\n  data_source       — \"live\" if Neon coverage matched, else \"seed\"\n\nStrict matching: an SKU only attaches when BOTH operator and region map.\nOperators without a public-pricing collector (colos, sovereign-AI,\nmost neoclouds) always render data_source=seed.","operationId":"map_datacenters_demo_map_datacenters_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Map Datacenters Demo Map Datacenters Get"}}}}}}},"/demo/gen-mix/{zone}":{"get":{"summary":"Generation Mix","description":"Generation mix + carbon intensity for an LMP zone.\n\n- Always returns seeded data from ``src.data_feeds.generation_mix``\n  (IPCC AR6 carbon factors + 2024 public-source mix).\n- When ``live=true`` AND ``EIA_API_KEY`` is set AND the zone maps to\n  a US balancing authority, tries the EIA live feed first and falls\n  back silently to seeded on failure.\n\nReturns 404 when the zone isn't in the seeded catalog.","operationId":"generation_mix_demo_gen_mix__zone__get","parameters":[{"name":"zone","in":"path","required":true,"schema":{"type":"string","title":"Zone"}},{"name":"live","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Live"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Generation Mix Demo Gen Mix  Zone  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/demo/gen-mix":{"get":{"summary":"Generation Mix List","description":"List all zones for which gen-mix data is available.","operationId":"generation_mix_list_demo_gen_mix_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Generation Mix List Demo Gen Mix Get"}}}}}}},"/demo/map/coverage":{"get":{"summary":"Map Coverage","description":"Coverage statistics per tier — drives the header coverage chip.\n\nReturns facilities, disclosed_capacity_mw, and operator counts for\nTier 1 (indexed) + Tier 2 (observable) + Tier 3 (known) + totals.","operationId":"map_coverage_demo_map_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Map Coverage Demo Map Coverage Get"}}}}}}},"/demo/map/lmp":{"get":{"summary":"Map Lmp","description":"GeoJSON FeatureCollection of seeded LMP zone centroids.\n\nSeeded $/MWh values anchored to 2024-25 public wholesale ranges.\nProd would wire this to ISO feeds (PJM Data Miner, ERCOT GMR,\nCAISO OASIS, EPEX Spot). Demo layer is illustrative only.","operationId":"map_lmp_demo_map_lmp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Map Lmp Demo Map Lmp Get"}}}}}}},"/demo/scenarios":{"get":{"summary":"Scenarios","description":"Named walkthrough scenarios — stakeholder-friendly index.\n\nEach entry carries a narrative + a ready-to-paste curl command.\nGrouped by persona: CFO (TCO), Procurement buyer, Contributor operator.","operationId":"scenarios_demo_scenarios_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Scenarios Demo Scenarios Get"}}}}}}}},"components":{"schemas":{"ActivateRequest":{"properties":{"index_id":{"type":"string","maxLength":64,"minLength":1,"title":"Index Id"},"activated_by":{"type":"string","maxLength":128,"minLength":2,"title":"Activated By","description":"Name/email/role of the operator"},"is_active":{"type":"boolean","title":"Is Active","description":"Set to false to deactivate","default":true}},"type":"object","required":["index_id","activated_by"],"title":"ActivateRequest","description":"Body for POST /_admin/activate. activated_by mirrors approved_by\nsemantics — human identity of the operator flipping the spec live."},"ApproveRequest":{"properties":{"index_id":{"type":"string","maxLength":128,"minLength":3,"title":"Index Id"},"approved_by":{"type":"string","maxLength":128,"minLength":2,"title":"Approved By","description":"Name/email/role of the approver"}},"type":"object","required":["index_id","approved_by"],"title":"ApproveRequest","description":"Body for POST /_admin/approve. approved_by is the human-readable\nidentity of the approver — name, email, or role string. Recorded\nimmutably on the spec for audit. Reviewers should sign in their own\nname so the approval chain is traceable."},"BacktestRequest":{"properties":{"spec":{"additionalProperties":true,"type":"object","title":"Spec"},"days":{"type":"integer","maximum":90.0,"minimum":1.0,"title":"Days","default":7},"bucket_seconds":{"type":"integer","maximum":86400.0,"minimum":300.0,"title":"Bucket Seconds","default":3600}},"type":"object","required":["spec"],"title":"BacktestRequest","description":"Backtest a spec without saving it. Returns a series of bucket-level\nmedians over the past `days` window for sparkline rendering. Uses\nraw_pricing.timestamp as the time axis (point-in-time per bucket)."},"BreakevenRequest":{"properties":{"sku":{"type":"string","title":"Sku"},"lease_term":{"type":"integer","exclusiveMinimum":0.0,"title":"Lease Term","description":"horizon in months"},"pue":{"type":"number","exclusiveMinimum":0.0,"title":"Pue"},"power_rate_kwh":{"type":"number","exclusiveMinimum":0.0,"title":"Power Rate Kwh"}},"type":"object","required":["sku","lease_term","pue","power_rate_kwh"],"title":"BreakevenRequest"},"BreakevenResponse":{"properties":{"breakeven_utilization":{"type":"number","title":"Breakeven Utilization"},"lease_term_used":{"type":"string","title":"Lease Term Used"},"term_clipped":{"type":"boolean","title":"Term Clipped"},"curve_maturity":{"type":"string","title":"Curve Maturity"}},"type":"object","required":["breakeven_utilization","lease_term_used","term_clipped","curve_maturity"],"title":"BreakevenResponse"},"BuyVsLeaseRequest":{"properties":{"sku":{"type":"string","title":"Sku"},"utilization":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Utilization"},"horizon_months":{"type":"integer","exclusiveMinimum":0.0,"title":"Horizon Months"},"pue":{"type":"number","exclusiveMinimum":0.0,"title":"Pue"},"power_rate_kwh":{"type":"number","exclusiveMinimum":0.0,"title":"Power Rate Kwh"}},"type":"object","required":["sku","utilization","horizon_months","pue","power_rate_kwh"],"title":"BuyVsLeaseRequest"},"BuyVsLeaseResponse":{"properties":{"buy_total_cost":{"type":"number","title":"Buy Total Cost"},"lease_total_cost":{"type":"number","title":"Lease Total Cost"},"savings_pct":{"type":"number","title":"Savings Pct"},"lease_term_used":{"type":"string","title":"Lease Term Used"},"term_clipped":{"type":"boolean","title":"Term Clipped"},"curve_maturity":{"type":"string","title":"Curve Maturity"},"components":{"additionalProperties":{"type":"number"},"type":"object","title":"Components"}},"type":"object","required":["buy_total_cost","lease_total_cost","savings_pct","lease_term_used","term_clipped","curve_maturity","components"],"title":"BuyVsLeaseResponse"},"CapacityIngestRequest":{"properties":{"observations":{"items":{"$ref":"#/components/schemas/CapacityObservation"},"type":"array","maxItems":10000,"minItems":1,"title":"Observations"}},"type":"object","required":["observations"],"title":"CapacityIngestRequest"},"CapacityObservation":{"properties":{"observed_at":{"type":"string","format":"date-time","title":"Observed At"},"canonical_sku":{"type":"string","title":"Canonical Sku","description":"Canonical FLOPS SKU id, e.g. 'B300-SXM7-288GB'"},"provider_region":{"type":"string","title":"Provider Region"},"pricing_model":{"type":"string","title":"Pricing Model","default":"on_demand"},"available_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Available Count"},"total_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Total Count"},"in_stock_units_per_hour":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"In Stock Units Per Hour"},"price_per_gpu_hour_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Price Per Gpu Hour Usd"},"rentability_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rentability Tier","description":"bookable_now | bookable_with_quote | reserve_only | depleted"},"measurement_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Measurement Method"},"raw_payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Raw Payload"}},"type":"object","required":["observed_at","canonical_sku","provider_region"],"title":"CapacityObservation","description":"One capacity observation. Mirrors what Hydrahost will emit per SKU/region."},"CleanupRequest":{"properties":{"unready_only":{"type":"boolean","title":"Unready Only","description":"If true, only match specs that have never published a tick.","default":true},"min_age_hours":{"type":"integer","minimum":0.0,"title":"Min Age Hours","description":"Only match specs created at least this many hours ago.","default":0},"pattern":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pattern","description":"ILIKE pattern on index_id (e.g. 'CUSTOM-X%' or '%TEST%'). Use SQL wildcards: % = any chars, _ = single char."},"owner_org":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Org","description":"Restrict to one owner_org (exact match)."},"dry_run":{"type":"boolean","title":"Dry Run","description":"If true, return the kill list but make no changes. Default safe.","default":true},"hard_delete":{"type":"boolean","title":"Hard Delete","description":"If true, DELETE FROM custom_index_spec (cascades to custom_index_values). If false, UPDATE is_active=FALSE.","default":false}},"type":"object","title":"CleanupRequest","description":"Filter criteria for bulk cleanup of test-pollution / abandoned specs.\n\nDefaults are conservative: dry_run=true (no destructive action), soft-\ndelete (recoverable via UPDATE is_active=TRUE), only specs that have\nNEVER published a tick. Tighten via pattern/owner_org/min_age_hours.\n\nTo actually destroy: pass dry_run=false. To hard-delete (cascades to\ncustom_index_values via FK ON DELETE CASCADE) pass hard_delete=true."},"ContribIntakeRequest":{"properties":{"org_name":{"type":"string","maxLength":200,"minLength":1,"title":"Org Name"},"contact_email":{"type":"string","maxLength":320,"minLength":3,"title":"Contact Email"},"contact_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Contact Name"},"data_offered":{"type":"string","maxLength":2000,"minLength":1,"title":"Data Offered","description":"Free-text description, e.g. 'H100 OD pricing for 5 EU regions, hourly snapshots'"},"volume_estimate":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Volume Estimate"},"sample_data_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Sample Data Url"},"notes":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["org_name","contact_email","data_offered"],"title":"ContribIntakeRequest"},"ContribIntakeUpdate":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"triaged_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Triaged By"}},"type":"object","title":"ContribIntakeUpdate"},"CreateOutreachRequest":{"properties":{"partner":{"type":"string","maxLength":200,"minLength":1,"title":"Partner"},"methodology_version":{"type":"string","maxLength":80,"minLength":1,"title":"Methodology Version","description":"Stable methodology slug at send time, e.g. 'flops-h100-od@v1.0.1'"},"contact_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Contact Name"},"contact_email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"title":"Contact Email"},"status":{"type":"string","title":"Status","description":"One of ('queued', 'sent', 'opened', 'replied', 'meeting_booked', 'passed', 'stalled')","default":"queued"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["partner","methodology_version"],"title":"CreateOutreachRequest"},"CreatePartnerRequest":{"properties":{"org_slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,40}$","title":"Org Slug","description":"Lowercase identifier — modular, hydrahost, ketul, ..."},"display_name":{"type":"string","title":"Display Name","description":"Human-readable org name"},"scope":{"type":"string","pattern":"^(public|customer|staff|admin|partner|internal)$","title":"Scope","default":"customer"},"commercial_tier":{"type":"string","pattern":"^(none|observer|contributor|partner|oem)$","title":"Commercial Tier","description":"Lightpaper pricing package — controls data depth, not page access.","default":"none"},"rate_limit_rpm":{"type":"integer","exclusiveMinimum":0.0,"title":"Rate Limit Rpm","description":"Org-wide ceiling. Per-user rate_limit overrides downward.","default":60},"dashboard_password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dashboard Password","description":"Plaintext password for org-wide dashboard login. Empty/null = no org-level dashboard credential (users created under this partner each get their own)."},"generate_api_key":{"type":"boolean","title":"Generate Api Key","description":"Mint an org-wide service-account API key. Most partners use per-user keys instead.","default":true},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["org_slug","display_name"],"title":"CreatePartnerRequest"},"CreateSpecRequest":{"properties":{"index_id":{"type":"string","maxLength":128,"minLength":3,"title":"Index Id","description":"Stable identifier, e.g. 'CUSTOM-HYDRAHOST-B300-NORDICS-APAC'"},"owner_org":{"type":"string","maxLength":64,"minLength":2,"title":"Owner Org"},"name":{"type":"string","maxLength":200,"minLength":2,"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"spec":{"additionalProperties":true,"type":"object","title":"Spec"},"visibility":{"type":"string","pattern":"^(private|public|partner)$","title":"Visibility","default":"private"}},"type":"object","required":["index_id","owner_org","name","spec"],"title":"CreateSpecRequest","description":"Payload for POST / — register a new custom index spec.\n\nThe full publisher worker (buildout #8) will compute ticks on a cron\nbased on the spec's cadence_seconds. Until then, partners can call\nPOST /{id}/recompute to compute a tick on-demand from the current\nnormalized_pricing snapshot."},"CreateUserRequest":{"properties":{"partner_slug":{"type":"string","title":"Partner Slug","description":"Existing partner_org.org_slug"},"user_slug":{"type":"string","pattern":"^[a-z0-9][a-z0-9_-]{0,40}$","title":"User Slug","description":"Per-person identifier (alice / bob)"},"display_name":{"type":"string","title":"Display Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"scope":{"type":"string","pattern":"^(public|customer|staff|admin|partner|internal)$","title":"Scope","default":"customer"},"rate_limit_rpm":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Rate Limit Rpm"},"dashboard_password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dashboard Password","description":"Plaintext password for dashboard login. Empty / null = API-only user."},"generate_api_key":{"type":"boolean","title":"Generate Api Key","default":true},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["partner_slug","user_slug","display_name"],"title":"CreateUserRequest"},"EditPartnerRequest":{"properties":{"scope":{"anyOf":[{"type":"string","pattern":"^(public|customer|staff|admin|partner|internal)$"},{"type":"null"}],"title":"Scope"},"commercial_tier":{"anyOf":[{"type":"string","pattern":"^(none|observer|contributor|partner|oem)$"},{"type":"null"}],"title":"Commercial Tier"},"rate_limit_rpm":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Rate Limit Rpm"},"display_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Display Name"},"notes":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Notes"}},"type":"object","title":"EditPartnerRequest","description":"Same shape as EditUserRequest, plus commercial_tier. org_slug is\nimmutable (changing it would break login URLs + audit attribution\n+ cached cookies). Use revoke+recreate if you really need a slug\nchange."},"EditUserRequest":{"properties":{"scope":{"anyOf":[{"type":"string","pattern":"^(public|customer|staff|admin|partner|internal)$"},{"type":"null"}],"title":"Scope"},"rate_limit_rpm":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Rate Limit Rpm"},"display_name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Display Name"},"email":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"title":"Email"},"notes":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Notes"}},"type":"object","title":"EditUserRequest","description":"All fields optional — only the ones present in the request body\nget applied. Distinguishes via Pydantic's exclude_unset so a\nmissing field stays unchanged vs a null field clears it (only\nnullable columns like email, notes accept null).\n\nFields you CAN edit here: scope, rate_limit_rpm, display_name,\nemail, notes. Fields you canNOT: user_slug (immutable identifier),\npartner_org_id (move-between-orgs needs a separate op),\napi_key_hash / dashboard_password_hash (use the dedicated rotate /\nset endpoints), failed_login_count / locked_until (use unlock)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"RegisterAliasRequest":{"properties":{"canonical_id":{"type":"string","title":"Canonical Id"},"alias":{"type":"string","title":"Alias"},"source":{"type":"string","title":"Source"}},"type":"object","required":["canonical_id","alias","source"],"title":"RegisterAliasRequest"},"RegisterFinishRequest":{"properties":{"label":{"type":"string","maxLength":80,"minLength":1,"title":"Label","description":"Human label like 'MacBook TouchID' or 'YubiKey 5C'."},"response":{"additionalProperties":true,"type":"object","title":"Response","description":"navigator.credentials.create() result as JSON."}},"type":"object","required":["label","response"],"title":"RegisterFinishRequest"},"RegisterStartRequest":{"properties":{"label":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Label","description":"Optional label for the credential, echoed back. The actual persisted label comes from the finish call."}},"type":"object","title":"RegisterStartRequest"},"SensitivityRequest":{"properties":{"exclude_providers":{"items":{"type":"string"},"type":"array","title":"Exclude Providers","description":"Provider slugs to drop. Empty list = baseline, identical to /latest."}},"type":"object","title":"SensitivityRequest","description":"Body for POST /{id}/sensitivity. exclude_providers is a list of\nprovider slugs (lowercased) to drop from the observation set before\naggregation. Used for OTC negotiation: 'show me the index without\ncounterparty X' so partners can argue an index is robust to losing\nany one source."},"SensitivityResponse":{"properties":{"baseline":{"$ref":"#/components/schemas/TCOComputeResponse"},"variables":{"items":{"$ref":"#/components/schemas/SensitivityVariable"},"type":"array","title":"Variables"},"methodology_doc":{"type":"string","title":"Methodology Doc"},"perturbation_count":{"type":"integer","title":"Perturbation Count"}},"type":"object","required":["baseline","variables","methodology_doc","perturbation_count"],"title":"SensitivityResponse"},"SensitivityVariable":{"properties":{"name":{"type":"string","title":"Name"},"label":{"type":"string","title":"Label"},"unit":{"type":"string","title":"Unit"},"baseline_value":{"type":"number","title":"Baseline Value"},"low_value":{"type":"number","title":"Low Value"},"high_value":{"type":"number","title":"High Value"},"baseline_tco":{"type":"number","title":"Baseline Tco"},"low_tco":{"type":"number","title":"Low Tco"},"high_tco":{"type":"number","title":"High Tco"},"delta_low":{"type":"number","title":"Delta Low"},"delta_high":{"type":"number","title":"Delta High"},"range_abs":{"type":"number","title":"Range Abs"},"source":{"type":"string","title":"Source"}},"type":"object","required":["name","label","unit","baseline_value","low_value","high_value","baseline_tco","low_tco","high_tco","delta_low","delta_high","range_abs","source"],"title":"SensitivityVariable"},"SetPasswordRequest":{"properties":{"password":{"type":"string","maxLength":256,"minLength":8,"title":"Password","description":"Plaintext password the admin types. Bcrypt-hashed server-side; never logged."}},"type":"object","required":["password"],"title":"SetPasswordRequest"},"TCOComputeRequest":{"properties":{"sku":{"type":"string","title":"Sku","description":"GPU SKU, e.g., 'h100_sxm5'"},"gpu_count":{"type":"integer","exclusiveMinimum":0.0,"title":"Gpu Count"},"utilization":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Utilization"},"power_rate_kwh":{"type":"number","exclusiveMinimum":0.0,"title":"Power Rate Kwh","description":"$/kWh"},"pue":{"type":"number","exclusiveMinimum":0.0,"title":"Pue"},"monthly_ops_budget":{"type":"number","minimum":0.0,"title":"Monthly Ops Budget"},"lifetime_months":{"type":"integer","exclusiveMinimum":0.0,"title":"Lifetime Months"},"purchase_price_per_gpu":{"type":"number","exclusiveMinimum":0.0,"title":"Purchase Price Per Gpu"}},"type":"object","required":["sku","gpu_count","utilization","power_rate_kwh","pue","monthly_ops_budget","lifetime_months","purchase_price_per_gpu"],"title":"TCOComputeRequest"},"TCOComputeResponse":{"properties":{"total_cph":{"type":"number","title":"Total Cph"},"acquisition_cph":{"type":"number","title":"Acquisition Cph"},"power_cph":{"type":"number","title":"Power Cph"},"cooling_cph":{"type":"number","title":"Cooling Cph"},"ops_cph":{"type":"number","title":"Ops Cph"},"component_pcts":{"additionalProperties":{"type":"number"},"type":"object","title":"Component Pcts"},"annualized_cost":{"type":"number","title":"Annualized Cost"}},"type":"object","required":["total_cph","acquisition_cph","power_cph","cooling_cph","ops_cph","component_pcts","annualized_cost"],"title":"TCOComputeResponse"},"TimingReadResponse":{"properties":{"sku":{"type":"string","title":"Sku"},"status":{"type":"string","title":"Status"},"curve_maturity":{"type":"string","title":"Curve Maturity"},"lambda_p50":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lambda P50"},"half_life_months":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Half Life Months"},"r_squared":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"R Squared"},"structural_break":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Structural Break"},"note":{"type":"string","title":"Note"}},"type":"object","required":["sku","status","curve_maturity","lambda_p50","half_life_months","r_squared","structural_break","note"],"title":"TimingReadResponse"},"TotpEnrollFinishRequest":{"properties":{"code":{"type":"string","maxLength":6,"minLength":6,"title":"Code","description":"6-digit code from the authenticator app."}},"type":"object","required":["code"],"title":"TotpEnrollFinishRequest"},"TotpEnrollStartRequest":{"properties":{"label":{"type":"string","maxLength":80,"minLength":1,"title":"Label","description":"Human label like 'iPhone Google Auth' or '1Password'.","default":"Authenticator"}},"type":"object","title":"TotpEnrollStartRequest"},"UpdateOutreachRequest":{"properties":{"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"contact_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contact Name"},"contact_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Contact Email"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"opened_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Opened At"},"replied_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Replied At"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"UpdateOutreachRequest"},"ValidateRequest":{"properties":{"spec":{"additionalProperties":true,"type":"object","title":"Spec"}},"type":"object","required":["spec"],"title":"ValidateRequest","description":"Spec payload for POST /validate. The shape mirrors what a partner\nwould submit when creating an index — without the DB-level fields\n(owner_org, visibility, name) that are write-path concerns."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebauthnRegisterRequest":{"properties":{"label":{"type":"string","maxLength":80,"minLength":1,"title":"Label","description":"Human label like 'MacBook TouchID' or 'YubiKey 5C nano'."},"response":{"additionalProperties":true,"type":"object","title":"Response","description":"navigator.credentials.create() result as JSON."}},"type":"object","required":["label","response"],"title":"WebauthnRegisterRequest"},"_ProbeIngestRequest":{"properties":{"vantage_region":{"type":"string","maxLength":10,"title":"Vantage Region","description":"Cloudflare PoP IATA, e.g. iad/lhr/fra"},"collection_run_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Collection Run Id","description":"UUID identifying this Worker run"},"probes":{"items":{"$ref":"#/components/schemas/_ProbeRecord"},"type":"array","maxItems":500,"title":"Probes"}},"type":"object","required":["vantage_region","probes"],"title":"_ProbeIngestRequest"},"_ProbeRecord":{"properties":{"provider":{"type":"string","maxLength":64,"title":"Provider"},"endpoint":{"type":"string","maxLength":512,"title":"Endpoint"},"http_status":{"anyOf":[{"type":"integer","maximum":999.0,"minimum":0.0},{"type":"null"}],"title":"Http Status"},"latency_ms":{"type":"number","maximum":120000.0,"minimum":0.0,"title":"Latency Ms"},"ok":{"type":"boolean","title":"Ok"},"error_class":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Error Class"}},"type":"object","required":["provider","endpoint","latency_ms","ok"],"title":"_ProbeRecord"}}}}