{"openapi":"3.1.0","info":{"title":"CrowdOS API","description":"Population intelligence, on demand. Query a persistent, census-grounded synthetic crowd of personas that read today's news, evolve daily, and remember. Calibrated to 91.9% Pew parity.","version":"1"},"paths":{"/{rest_of_path}":{"options":{"summary":"Preflight Handler","operationId":"preflight_handler__rest_of_path__options","parameters":[{"name":"rest_of_path","in":"path","required":true,"schema":{"type":"string","title":"Rest Of Path"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations":{"post":{"tags":["simulations"],"summary":"Create Simulation","operationId":"create_simulation_api_v1_simulations_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/SimulationCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["simulations"],"summary":"List My Simulations","description":"List the authenticated user's simulations, newest first, with only the\nfields the dashboard cards need.\n\nThis is the server-side source of truth for the dashboard so studies\nsurvive a device switch / cleared localStorage — previously the card list\nwas localStorage-only, so Pulse + Screening runs vanished on a new device.\nAd Lab runs live in this table too (``mode=\"ad_lab\"``), so they're\nincluded. Returns a slim projection (no fat ``results`` / cost JSONB) to\nkeep the payload small.","operationId":"list_my_simulations_api_v1_simulations_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/normalize":{"post":{"tags":["simulations"],"summary":"Normalize Topic","description":"Rewrite a raw user topic into a clean stance statement.\n\nThe frontend calls this when the user hits \"Preview question\" or\nsubmits the creation form — the response is shown back to the user\nwith an edit affordance before the sim is actually launched.\n\nAlways returns a usable NormalizedQuestion — on LLM failure the\nfallback just echoes the topic so the UX never blocks.","operationId":"normalize_topic_api_v1_simulations_normalize_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/NormalizeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/brief":{"post":{"tags":["simulations"],"summary":"Moderator Brief","description":"Run the Moderator Intake: raw topic -> structured ResearchBrief.\n\nThis replaces the old normalizer as the primary intake step for\nScreening Room and comparison flows. The moderator LLM (Sonnet)\ndecides what the user is actually trying to learn, who the target\naudience is, what \"good\" looks like, and produces a verbatim\nparagraph every agent will see before answering.\n\nThe frontend calls this after the user types their question and\nshows the returned brief in an editable confirm modal — the user\ncan tweak goal / audience / briefing before launching the sim.","operationId":"moderator_brief_api_v1_simulations_brief_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/BriefRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}":{"get":{"tags":["simulations"],"summary":"Get Simulation","operationId":"get_simulation_api_v1_simulations__sim_id__get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/status":{"get":{"tags":["simulations"],"summary":"Get Simulation Status","description":"Lightweight status check for polling.\n\nReturns ``{id, status, mode, created_at}`` — never pulls the\n``results`` JSONB blob (which can be 5+ MB on a 250-agent run).\nFrontends should poll this while the sim is\n``pending``/``running`` and only fetch the full record once\nstatus transitions to ``complete`` or ``failed``.","operationId":"get_simulation_status_api_v1_simulations__sim_id__status_get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/run":{"post":{"tags":["simulations"],"summary":"Run Simulation","operationId":"run_simulation_api_v1_simulations__sim_id__run_post","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/stream":{"get":{"tags":["simulations"],"summary":"Stream Simulation","description":"SSE endpoint: stream live progress for a simulation.\n\nPR-B architecture (May 2026): the SSE handler is purely a tail.\nSim execution lives in services.sim_supervisor.run_sim_to_completion\nand runs as a detached background task via services.sim_runner.\nDisconnecting from this stream does NOT cancel the underlying sim —\nthe user can navigate away, the task keeps running, results land\nin DB, and reconnecting (or polling GET /{id}, or watching the\ncockpit) all see the same in-flight state.\n\nIdempotent: calling /stream twice on the same sim_id won't\ndouble-launch — sim_runner.launch is a no-op when a task is\nalready in flight for that id.\n\nEvents:\n    All modes: started, progress, complete | error | cancelled\n    Voting:    + agent_result\n    Debate:    + round_start, round_complete","operationId":"stream_simulation_api_v1_simulations__sim_id__stream_get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"JWT access token — required because EventSource can't send Authorization headers.","title":"Token"},"description":"JWT access token — required because EventSource can't send Authorization headers."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/cancel":{"post":{"tags":["simulations"],"summary":"Cancel Simulation","description":"Explicitly cancel a running simulation.\n\nPR-B introduced a hard separation between \"user closed the tab\"\n(sim continues) and \"user explicitly cancels\" (sim stops + wallet\nrefunds). This endpoint is the second case.\n\nAuthorization accepts a JWT (web app) or a crowd_ API key (the\npublished MCP client cancels with a key, not a JWT) and then\nrequires owner-or-admin — otherwise anyone who guessed a running\nsim_id could abort another user's paid study and trigger a refund\non their behalf.\n\nReturns:\n    200 with {\"status\": \"cancelled\"} on success\n    409 if the sim is already terminal (complete / failed / cancelled)\n    404 if no such sim exists (or the caller doesn't own it)\n    500 if cancellation succeeded but wallet refund failed (the row\n        is still marked cancelled; an operator should reconcile)","operationId":"cancel_simulation_api_v1_simulations__sim_id__cancel_post","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/validate":{"post":{"tags":["simulations"],"summary":"Validate Against Real Data","description":"Compare a completed voting sim's sentiment to real panel data.\n\nReturns a structured report with MAE, RMSE, Pearson correlation,\nper-bucket deltas, a parity percentage (0-100), and a letter grade\n(A-F). The result is persisted back into `sim.results.validation`\nso the frontend detail page can surface it later without re-hitting\nthis endpoint.\n\nSend either percentages (summing to 100) or raw respondent counts\nin `real_distribution` — the server normalizes either way. Only\nthe `positive` / `neutral` / `negative` keys are used for\ncomparison. Unknown keys are ignored.\n\n400 if the sim isn't a voting/comparison result with a sentiment\nsummary; 404 if the sim doesn't exist.","operationId":"validate_against_real_data_api_v1_simulations__sim_id__validate_post","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/pdf":{"get":{"tags":["simulations"],"summary":"Export Simulation Pdf","description":"Generate and return a PDF report for a completed simulation.","operationId":"export_simulation_pdf_api_v1_simulations__sim_id__pdf_get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"JWT access token — required because PDF anchors can't send Authorization headers.","title":"Token"},"description":"JWT access token — required because PDF anchors can't send Authorization headers."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/upload-for-comparison":{"post":{"tags":["simulations"],"summary":"Upload For Comparison","description":"Stash an uploaded file for comparison / screening-room mode.\n\nThis endpoint used to immediately run a Gemini / Qwen-VL / GPT-4o\nanalysis to generate a description — which wasted tokens every time\na user swapped files before launching. It now only validates the\nfile and pushes raw bytes into the ``screening-uploads`` Supabase\nbucket (or a local tmp fallback). All LLM work is deferred to\nsimulation launch, where the uploaded bytes are re-hydrated and\nsent natively to Gemini / Qwen-VL agents via the media_store\nservice.\n\nReturns an ``upload_id`` the frontend attaches to each option so\nthe launch path can find the file.\n\nSupported: PNG/JPG/WEBP/GIF (images), MP4/MOV/WebM/AVI/MKV/M4V\n(videos), PDF/TXT/MD/CSV (documents). Max 10 MB images/docs,\n100 MB video.","operationId":"upload_for_comparison_api_v1_simulations_upload_for_comparison_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_for_comparison_api_v1_simulations_upload_for_comparison_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/supported-formats":{"get":{"tags":["simulations"],"summary":"Get Supported Formats","description":"Return supported file formats and limitations for comparison uploads.","operationId":"get_supported_formats_api_v1_simulations_supported_formats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/simulations/screening-room/tiers":{"get":{"tags":["simulations"],"summary":"Get Screening Room Tiers","description":"Return Screening Room tier metadata for the frontend tier selector.\n\nEach tier describes its cost, model mix, and whether expert reports\nare included. The ``available`` flag indicates whether the required\nAPI keys are configured on the server.","operationId":"get_screening_room_tiers_api_v1_simulations_screening_room_tiers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/simulations/{sim_id}/screening/chat":{"post":{"tags":["simulations"],"summary":"Screening Chat","description":"Ask a follow-up question about a completed Deep Review screening.\n\nThe first 3 questions are free. Subsequent questions cost $0.25 each\nand are tracked against the simulation's chat.used_questions counter.","operationId":"screening_chat_api_v1_simulations__sim_id__screening_chat_post","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScreeningChatRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/screening/pdf":{"get":{"tags":["simulations"],"summary":"Screening Pdf Report","description":"Generate a professional PDF report of a completed screening.\n\nReturns a formatted multi-page PDF with:\n- Executive summary\n- Leaderboard visualization\n- Full synthesis memo\n- Expert analyst reports\n- Demographic breakdown\n- Sample viewer opinions\n- Debate round takeaways (Deep Review)","operationId":"screening_pdf_report_api_v1_simulations__sim_id__screening_pdf_get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"JWT access token — required because PDF anchors can't send Authorization headers.","title":"Token"},"description":"JWT access token — required because PDF anchors can't send Authorization headers."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/simulations/{sim_id}/followup":{"post":{"tags":["simulations"],"summary":"Followup Question","description":"Ask the moderator a follow-up question about a completed simulation.\n\nThe moderator has full context of the simulation results — topic,\nsentiment summary, and every agent's response. It can answer questions\nabout specific agents, patterns, demographics, or help interpret\nthe results. Questions must be about the simulation results only.","operationId":"followup_question_api_v1_simulations__sim_id__followup_post","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"token","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowUpRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FollowUpResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/check":{"get":{"tags":["admin-console"],"summary":"Admin Check","description":"Verify the caller is a logged-in admin. Returns identity for the UI.\n\nThe frontend admin login page calls this immediately after a\nsuccessful Supabase signin. A 200 means proceed to the console;\na 403 (raised by ``require_admin``) means the user is signed in\nbut not an admin and should be told so.","operationId":"admin_check_api_v1_admin_check_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Check Api V1 Admin Check Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/overview":{"get":{"tags":["admin-console"],"summary":"Admin Overview","description":"Operational KPIs for the Overview tab. Read-only.\n\nRuns the five table reads in parallel via asyncio.to_thread, and\ndrops the simulations.results JSONB from the projection — that\ncolumn is megabytes per sim and isn't needed for the overview.\nPre-tier-routing legacy sims that relied on results.screening_room\nfor classification will now be counted as voting/debate, which is\na fine tradeoff for dashboard speed.","operationId":"admin_overview_api_v1_admin_overview_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Overview Api V1 Admin Overview Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/revenue/summary":{"get":{"tags":["admin-console"],"summary":"Admin Revenue Summary","description":"MRR, ARR, MTD / rolling-N revenue, wallet liability, top spenders,\nplan distribution, and recent ledger. Read-only.","operationId":"admin_revenue_summary_api_v1_admin_revenue_summary_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Revenue Summary Api V1 Admin Revenue Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/funnel":{"get":{"tags":["admin-console"],"summary":"Admin Users Funnel","operationId":"admin_users_funnel_api_v1_admin_users_funnel_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Users Funnel Api V1 Admin Users Funnel Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/cost-center":{"get":{"tags":["admin-console"],"summary":"Admin Llm Cost Center","operationId":"admin_llm_cost_center_api_v1_admin_llm_cost_center_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Llm Cost Center Api V1 Admin Llm Cost Center Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/cost-center/task/{task_name}":{"get":{"tags":["admin-console"],"summary":"Admin Llm Task Drilldown","description":"Drill-down for a BY TASK row in the LLM Cost Center.\n\nReturns the list of completed user-driven sims (within the time\nwindow) whose mode plausibly produced calls under this task,\nwith per-sim user_email + topic + cost + timestamp. Cost won't\nexactly equal the ledger total — ledger includes retries, fails,\nand shared overhead — but the per-sim list answers 'who ran what\nwhen' for that task.","operationId":"admin_llm_task_drilldown_api_v1_admin_llm_cost_center_task__task_name__get","parameters":[{"name":"task_name","in":"path","required":true,"schema":{"type":"string","title":"Task Name"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":30,"title":"Days"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Llm Task Drilldown Api V1 Admin Llm Cost Center Task  Task Name  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/cost-center/model/{model_or_provider}":{"get":{"tags":["admin-console"],"summary":"Admin Llm Model Drilldown","description":"Drill-down for a BY PROVIDER row or model_table row.\n\nReads llm_usage_ledger filtered to rows whose `model` either equals\nthe path arg verbatim (model row clicked) or starts with `arg/`\n(provider prefix clicked, e.g. 'openai' matches 'openai/gpt-4o').\n\nReturns 30d totals, a daily-cost time series, and a by-task split\nfor that model/provider. The ledger has no sim_id or user_id, so\nper-user attribution isn't possible at this level — this surface\nanswers 'how is this model's cost split across tasks and time'.","operationId":"admin_llm_model_drilldown_api_v1_admin_llm_cost_center_model__model_or_provider__get","parameters":[{"name":"model_or_provider","in":"path","required":true,"schema":{"type":"string","title":"Model Or Provider"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Llm Model Drilldown Api V1 Admin Llm Cost Center Model  Model Or Provider  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/crowd/health":{"get":{"tags":["admin-console"],"summary":"Admin Crowd Health","operationId":"admin_crowd_health_api_v1_admin_crowd_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Crowd Health Api V1 Admin Crowd Health Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/api/usage":{"get":{"tags":["admin-console"],"summary":"Admin Api Usage","operationId":"admin_api_usage_api_v1_admin_api_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Api Usage Api V1 Admin Api Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/system/health":{"get":{"tags":["admin-console"],"summary":"Admin System Health","operationId":"admin_system_health_api_v1_admin_system_health_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin System Health Api V1 Admin System Health Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/system/latency":{"get":{"tags":["admin-console"],"summary":"Admin System Latency","description":"Aggregate in-process request-latency snapshot.\n\nReads from the ring buffer populated by the ASGI middleware in\nmain.py. Returns p50/p95/p99/max + count + error rate per\n(method, route-template) over the current window. Resets on\nredeploy — this is a \"what's slow right now\" surface, not an APM.","operationId":"admin_system_latency_api_v1_admin_system_latency_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin System Latency Api V1 Admin System Latency Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/jobs":{"get":{"tags":["admin-console"],"summary":"Admin Jobs List","description":"Paginated job feed across all users.\n\nFilters: ``status`` matches simulations.status verbatim. ``q``\nis a case-insensitive substring search across topic + user\nemail. Ordering: newest first.","operationId":"admin_jobs_list_api_v1_admin_jobs_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","maxLength":32,"default":"","title":"Status"}},{"name":"q","in":"query","required":false,"schema":{"type":"string","maxLength":200,"default":"","title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Jobs List Api V1 Admin Jobs Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/jobs/{job_id}":{"get":{"tags":["admin-console"],"summary":"Admin Jobs Get","description":"Full job detail: config + results + cost breakdown.","operationId":"admin_jobs_get_api_v1_admin_jobs__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Jobs Get Api V1 Admin Jobs  Job Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/jobs/{job_id}/cancel":{"post":{"tags":["admin-console"],"summary":"Admin Jobs Cancel","description":"Mark a running / queued job as cancelled and refund the wallet\ndebit if one was charged. Audit-logged.","operationId":"admin_jobs_cancel_api_v1_admin_jobs__job_id__cancel_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Jobs Cancel Api V1 Admin Jobs  Job Id  Cancel Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/jobs/{job_id}/retry":{"post":{"tags":["admin-console"],"summary":"Admin Jobs Retry","description":"Reset a failed job to queued so the worker re-picks it up.\nDoes not re-debit the wallet — the original debit remains.","operationId":"admin_jobs_retry_api_v1_admin_jobs__job_id__retry_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Jobs Retry Api V1 Admin Jobs  Job Id  Retry Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/jobs/{job_id}/refund":{"post":{"tags":["admin-console"],"summary":"Admin Jobs Refund","description":"Issue a wallet refund for a completed job without changing\nits status. For \"we delivered but the result was bad\" cases.","operationId":"admin_jobs_refund_api_v1_admin_jobs__job_id__refund_post","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Jobs Refund Api V1 Admin Jobs  Job Id  Refund Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/flags":{"get":{"tags":["admin-console"],"summary":"Admin Flags List","operationId":"admin_flags_list_api_v1_admin_flags_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Flags List Api V1 Admin Flags Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin-console"],"summary":"Admin Flags Create","operationId":"admin_flags_create_api_v1_admin_flags_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Flags Create Api V1 Admin Flags Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/flags/{key}":{"patch":{"tags":["admin-console"],"summary":"Admin Flags Update","operationId":"admin_flags_update_api_v1_admin_flags__key__patch","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Flags Update Api V1 Admin Flags  Key  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/audit":{"get":{"tags":["admin-console"],"summary":"Admin Audit List","operationId":"admin_audit_list_api_v1_admin_audit_get","parameters":[{"name":"action","in":"query","required":false,"schema":{"type":"string","maxLength":64,"default":"","title":"Action"}},{"name":"admin_email","in":"query","required":false,"schema":{"type":"string","maxLength":120,"default":"","title":"Admin Email"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Audit List Api V1 Admin Audit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/disable":{"post":{"tags":["admin-console"],"summary":"Admin User Disable","operationId":"admin_user_disable_api_v1_admin_users__user_id__disable_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User Disable Api V1 Admin Users  User Id  Disable Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/enable":{"post":{"tags":["admin-console"],"summary":"Admin User Enable","operationId":"admin_user_enable_api_v1_admin_users__user_id__enable_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User Enable Api V1 Admin Users  User Id  Enable Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/promote":{"post":{"tags":["admin-console"],"summary":"Admin User Promote","operationId":"admin_user_promote_api_v1_admin_users__user_id__promote_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User Promote Api V1 Admin Users  User Id  Promote Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/demote":{"post":{"tags":["admin-console"],"summary":"Admin User Demote","operationId":"admin_user_demote_api_v1_admin_users__user_id__demote_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User Demote Api V1 Admin Users  User Id  Demote Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/api-keys/{key_id}/revoke":{"post":{"tags":["admin-console"],"summary":"Admin Api Key Revoke","operationId":"admin_api_key_revoke_api_v1_admin_api_keys__key_id__revoke_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Api Key Revoke Api V1 Admin Api Keys  Key Id  Revoke Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/api-keys/{key_id}/disable":{"post":{"tags":["admin-console"],"summary":"Admin Api Key Disable","description":"Admin kill switch — pause a key without revoking the user's record.\n\nDiffers from revoke in that the user can't restore the key (it's\nstill ``is_active=true``); only an admin can re-enable it. Use for\nsuspected-leak / abuse-investigation scenarios.","operationId":"admin_api_key_disable_api_v1_admin_api_keys__key_id__disable_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Api Key Disable Api V1 Admin Api Keys  Key Id  Disable Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/api-keys/{key_id}/enable":{"post":{"tags":["admin-console"],"summary":"Admin Api Key Enable","description":"Lift the admin kill switch on a key. Doesn't change ``is_active``.","operationId":"admin_api_key_enable_api_v1_admin_api_keys__key_id__enable_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Api Key Enable Api V1 Admin Api Keys  Key Id  Enable Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/billing-rates":{"get":{"tags":["admin-console"],"summary":"Admin Billing Rates List","operationId":"admin_billing_rates_list_api_v1_admin_billing_rates_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Billing Rates List Api V1 Admin Billing Rates Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/billing-rates/{rate_key}":{"put":{"tags":["admin-console"],"summary":"Admin Billing Rates Update","operationId":"admin_billing_rates_update_api_v1_admin_billing_rates__rate_key__put","parameters":[{"name":"rate_key","in":"path","required":true,"schema":{"type":"string","title":"Rate Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Billing Rates Update Api V1 Admin Billing Rates  Rate Key  Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/export/jobs.csv":{"get":{"tags":["admin-console"],"summary":"Admin Export Jobs Csv","operationId":"admin_export_jobs_csv_api_v1_admin_export_jobs_csv_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","maxLength":32,"default":"","title":"Status"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":1,"default":5000,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/export/users.csv":{"get":{"tags":["admin-console"],"summary":"Admin Export Users Csv","operationId":"admin_export_users_csv_api_v1_admin_export_users_csv_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":1,"default":5000,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/errors/recent":{"get":{"tags":["admin-console"],"summary":"Admin Errors Recent","description":"Surface recent failed simulations as a stand-in error log\nuntil a dedicated error_log table lands. Returns the last N\nsims with status='failed' inside the look-back window.","operationId":"admin_errors_recent_api_v1_admin_errors_recent_get","parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"default":24,"title":"Hours"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Errors Recent Api V1 Admin Errors Recent Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/census-drift":{"get":{"tags":["admin-console"],"summary":"Admin Census Drift","description":"Diagnostic view of how far the persistent crowd has drifted\nfrom baseline census distributions. Read-only — does not rebalance.\nOperator decides whether to re-seed.","operationId":"admin_census_drift_api_v1_admin_census_drift_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Census Drift Api V1 Admin Census Drift Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/active":{"get":{"tags":["admin-console"],"summary":"Admin Calibration Active","description":"Return every active calibration coefficient — what the runtime\nis using right now.","operationId":"admin_calibration_active_api_v1_admin_calibration_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Active Api V1 Admin Calibration Active Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/{topic_id}/versions":{"get":{"tags":["admin-console"],"summary":"Admin Calibration Versions","description":"Every version recorded for a topic, newest first.","operationId":"admin_calibration_versions_api_v1_admin_calibration__topic_id__versions_get","parameters":[{"name":"topic_id","in":"path","required":true,"schema":{"type":"string","title":"Topic Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Versions Api V1 Admin Calibration  Topic Id  Versions Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/history":{"get":{"tags":["admin-console"],"summary":"Admin Calibration History","description":"Append-only audit trail of every coefficient mutation.","operationId":"admin_calibration_history_api_v1_admin_calibration_history_get","parameters":[{"name":"topic_id","in":"query","required":false,"schema":{"type":"string","maxLength":128,"default":"","title":"Topic Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration History Api V1 Admin Calibration History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/upsert":{"post":{"tags":["admin-console"],"summary":"Admin Calibration Upsert","description":"Promote a candidate coefficient. Body:\n{\"topic_id\": \"...\", \"bias\": {...}, \"source_run_id\": \"...\",\n \"stance_statement\": \"...\", \"note\": \"...\"}","operationId":"admin_calibration_upsert_api_v1_admin_calibration_upsert_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Upsert Api V1 Admin Calibration Upsert Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/{topic_id}/rollback":{"post":{"tags":["admin-console"],"summary":"Admin Calibration Rollback","operationId":"admin_calibration_rollback_api_v1_admin_calibration__topic_id__rollback_post","parameters":[{"name":"topic_id","in":"path","required":true,"schema":{"type":"string","title":"Topic Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Rollback Api V1 Admin Calibration  Topic Id  Rollback Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/{topic_id}/deactivate":{"post":{"tags":["admin-console"],"summary":"Admin Calibration Deactivate","operationId":"admin_calibration_deactivate_api_v1_admin_calibration__topic_id__deactivate_post","parameters":[{"name":"topic_id","in":"path","required":true,"schema":{"type":"string","title":"Topic Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Deactivate Api V1 Admin Calibration  Topic Id  Deactivate Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/candidates":{"get":{"tags":["admin-console"],"summary":"Admin Calibration Candidates","description":"Mine `study_outcomes` ground-truth for candidate coefficient\nrefreshes. Read-only — operator promotes via /calibration/upsert.","operationId":"admin_calibration_candidates_api_v1_admin_calibration_candidates_get","parameters":[{"name":"days_window","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":7,"default":90,"title":"Days Window"}},{"name":"min_samples","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":2,"default":5,"title":"Min Samples"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Calibration Candidates Api V1 Admin Calibration Candidates Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/behavior":{"get":{"tags":["admin-console"],"summary":"Admin User Behavior","description":"Aggregate per-user behavior analytics for the User Detail panel.\n\nPulls simulations, engagement events, page views, wallet summary,\nsaved cohorts, follow-up chat questions, and topic keywords. All\nin one round-trip via parallel reads.","operationId":"admin_user_behavior_api_v1_admin_users__user_id__behavior_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User Behavior Api V1 Admin Users  User Id  Behavior Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/view-as/cockpit":{"get":{"tags":["admin-console"],"summary":"Admin User View As Cockpit","description":"Return the dashboard cockpit projection for the target user.\n\nIdentical shape to ``GET /api/v1/dashboard/cockpit`` — the same\nprojector renders both. The admin console embeds the result in a\nread-only panel. No mutation paths are exposed here; this is a\npure read.","operationId":"admin_user_view_as_cockpit_api_v1_admin_users__user_id__view_as_cockpit_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User View As Cockpit Api V1 Admin Users  User Id  View As Cockpit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/view-as/studies":{"get":{"tags":["admin-console"],"summary":"Admin User View As Studies","description":"Return the target user's recent studies, customer-redacted.\n\nApplies ``redact_results_for_customer`` to every row so the admin\nsees the same anonymized payload the user would. We feed the\nuser's plan into redaction so the billed amounts match what they'd\nsee (Pro discount applied where relevant). Admin tabs that need\nraw data still read directly from Supabase elsewhere; this\nendpoint deliberately re-runs redaction to be a faithful\n\"view as user\" surface.","operationId":"admin_user_view_as_studies_api_v1_admin_users__user_id__view_as_studies_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin User View As Studies Api V1 Admin Users  User Id  View As Studies Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/sources":{"get":{"tags":["admin"],"summary":"List Benchmark Sources","description":"Return every registered benchmark source with its full metadata.\n\nThis is what the admin dashboard renders as the \"Sources\" list.\nEach entry carries its origin URL, license, retrieval date, and\nthe list of presets it covers — so provenance is one click away.","operationId":"list_benchmark_sources_api_v1_admin_benchmarks_sources_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/sources/{source_id}":{"get":{"tags":["admin"],"summary":"Get Benchmark Source","description":"Return one source file's full parsed contents including per-preset data.","operationId":"get_benchmark_source_api_v1_admin_benchmarks_sources__source_id__get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete Benchmark Source","description":"Delete a source file and hot-reload.\n\nAfter deletion, any presets that were bound to this source fall\nback to the hardcoded baseline or another source (if one covers\nthe same preset).","operationId":"delete_benchmark_source_api_v1_admin_benchmarks_sources__source_id__delete","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/coverage":{"get":{"tags":["admin"],"summary":"Get Benchmark Coverage","description":"Return the coverage matrix for the admin dashboard.\n\nOne row per demographic preset showing which source (if any)\nprovides its OCEAN and which provides its demographics. Frontend\nrenders this as a grid with color-coded cells.","operationId":"get_benchmark_coverage_api_v1_admin_benchmarks_coverage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/errors":{"get":{"tags":["admin"],"summary":"Get Benchmark Load Errors","description":"Return errors from the most recent benchmark load attempt.\n\nEmpty array on a clean load. When non-empty, the admin dashboard\nsurfaces these as warnings so malformed files never silently\ndegrade the product.","operationId":"get_benchmark_load_errors_api_v1_admin_benchmarks_errors_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/reload":{"post":{"tags":["admin"],"summary":"Reload Benchmark Sources","description":"Re-scan the sources directory and hot-apply overrides.\n\nCalled after a file upload, a git pull, or an ETL script run.\nRebuilds the merged state from scratch — cheap and idempotent.","operationId":"reload_benchmark_sources_api_v1_admin_benchmarks_reload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/upload":{"post":{"tags":["admin"],"summary":"Upload Benchmark Source","description":"Upload a new benchmark source JSON file.\n\nThe file is parsed, validated against the schema, saved to\n`benchmarks/sources/<source_id>.json`, and the loader is immediately\nre-run so the new data takes effect without a restart. Overwrites\nany existing source with the same id.","operationId":"upload_benchmark_source_api_v1_admin_benchmarks_upload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_benchmark_source_api_v1_admin_benchmarks_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/refreshable":{"get":{"tags":["admin"],"summary":"List Refreshable Sources","description":"Return which benchmark sources have an ETL fetcher registered.\n\nThe admin frontend uses this to show/hide the REFRESH button on\neach source card — only sources with a registered ETL script get one.","operationId":"list_refreshable_sources_api_v1_admin_benchmarks_refreshable_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/benchmarks/sources/{source_id}/refresh":{"post":{"tags":["admin"],"summary":"Refresh Benchmark Source","description":"Re-fetch a single benchmark source from its public API.\n\nRuns the registered ETL script, writes the updated JSON file,\nand hot-reloads the benchmark overlay. This is the one-click\n\"update data\" button in the admin Data tab.","operationId":"refresh_benchmark_source_api_v1_admin_benchmarks_sources__source_id__refresh_post","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/presets":{"get":{"tags":["admin"],"summary":"List Calibration Presets","description":"Return the list of demographic presets that have a census benchmark.\n\nUncalibrated presets are returned separately so the frontend can\ndistinguish \"this preset has a calibration score\" from \"this preset\nexists but no published benchmark has been wired up yet.\"","operationId":"list_calibration_presets_api_v1_admin_calibration_presets_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/calibration/{preset_key}":{"get":{"tags":["admin"],"summary":"Get Calibration Report","description":"Run a calibration report for a specific demographic preset.\n\nGenerates `sample_size` synthetic personas from the preset, then\ncompares their age/gender/education/income distributions against\nthe published census benchmark for that preset. Returns a report\nwith per-dimension similarity scores and an overall grade (A-F).\n\nIf the preset has no benchmark yet, returns 400 with a descriptive\nerror. Cap sample_size at 2000 to keep the endpoint snappy.","operationId":"get_calibration_report_api_v1_admin_calibration__preset_key__get","parameters":[{"name":"preset_key","in":"path","required":true,"schema":{"type":"string","title":"Preset Key"}},{"name":"sample_size","in":"query","required":false,"schema":{"type":"integer","default":500,"title":"Sample Size"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/archetypes":{"get":{"tags":["admin"],"summary":"List Archetypes","description":"List all persistent archetypes with summary stats.\n\nPass ``?include_archived=true`` to include rows for retired presets\n(hong_kong, south_africa, gen_x, boomers as of May 2026). Defaults\nto active-only so the count matches the active swarm size.","operationId":"list_archetypes_api_v1_admin_archetypes_get","parameters":[{"name":"include_archived","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Archived"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/archetypes/{archetype_id}":{"get":{"tags":["admin"],"summary":"Get Archetype","description":"Get full archetype with persona, memory, opinion log, and recent posts.","operationId":"get_archetype_api_v1_admin_archetypes__archetype_id__get","parameters":[{"name":"archetype_id","in":"path","required":true,"schema":{"type":"string","title":"Archetype Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/archetypes/seed":{"post":{"tags":["admin"],"summary":"Seed Archetypes","description":"Seed initial persistent archetypes.\n\nDefaults to the V2 target of 500 archetypes spread across all 27\npresets. Skips if any archetypes already exist — use\n`/archetypes/top-up` to grow an existing population.","operationId":"seed_archetypes_api_v1_admin_archetypes_seed_post","parameters":[{"name":"count","in":"query","required":false,"schema":{"type":"integer","default":500,"title":"Count"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/archetypes/top-up":{"post":{"tags":["admin"],"summary":"Top Up Archetypes Endpoint","description":"Grow the persistent archetype swarm to `target_count`.\n\nIdempotent: if the population is already at or above the target,\nreturns `status: \"skipped\"` and adds nothing. Otherwise generates\nthe delta using the scaled V2 distribution (500 across all 27\npresets), prioritizing presets that are currently under-represented.\n\nUsed for ratcheting the swarm up without losing any existing\nmemory / opinion history / relationships.","operationId":"top_up_archetypes_endpoint_api_v1_admin_archetypes_top_up_post","parameters":[{"name":"target_count","in":"query","required":false,"schema":{"type":"integer","default":500,"title":"Target Count"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/daily-cycle":{"post":{"tags":["admin"],"summary":"Trigger Daily Cycle","description":"Kick off the daily evolution cycle in the background.\n\nThe cycle takes 1–5 minutes for 50 archetypes — way past Railway's HTTP\ntimeout — so by default this endpoint returns immediately and the cycle\nruns in a background task. Poll `GET /api/v1/admin/daily-cycle/status`\nto see progress and the final result.\n\nQuery params:\n  - `include_social=false` skips the social phase (Phase 1 only)\n  - `wait=true` blocks until the cycle finishes (useful for tests; not\n    recommended over the public API since the proxy will time out).\n\nThe operator's email is captured into both daily_cycle_runs.details\nand admin_audit_log so a manual trigger is distinguishable from a\nscheduler-fired one.","operationId":"trigger_daily_cycle_api_v1_admin_daily_cycle_post","parameters":[{"name":"include_social","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Social"}},{"name":"wait","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Wait"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/daily-cycle/status":{"get":{"tags":["admin"],"summary":"Get Daily Cycle Status","description":"Return the most recent daily cycle's state.\n\nStatus values:\n  - `never_run`  — process started fresh, no cycle has fired yet\n  - `running`    — a cycle is currently in progress (started_at set, finished_at null)\n  - `complete`   — most recent cycle finished successfully (result populated)\n  - `failed`     — most recent cycle crashed (error populated with traceback)\n\nProcess-local — if Railway restarted the worker mid-cycle, this resets\nto `never_run` until the next trigger or scheduler tick.","operationId":"get_daily_cycle_status_api_v1_admin_daily_cycle_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/news-feed":{"get":{"tags":["admin"],"summary":"Get News Feed","description":"Return the live headlines that the daily cycle would consume right now.\n\nLets admins verify that persistent agents are reading real news, not\nplaceholder content. Returns headline objects with source/url/published_at.","operationId":"get_news_feed_api_v1_admin_news_feed_get","parameters":[{"name":"count","in":"query","required":false,"schema":{"type":"integer","default":10,"title":"Count"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/social-feed":{"get":{"tags":["admin"],"summary":"Get Social Feed","description":"Return recent posts from the persistent archetype swarm.\n\nJoins against persistent_archetypes so each post includes the poster's\nname and preset without forcing the frontend to do N+1 lookups. Use\n`post_type` to filter to \"opinion\", \"reply\", or \"reaction\".","operationId":"get_social_feed_api_v1_admin_social_feed_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"post_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Post Type"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/stats":{"get":{"tags":["admin"],"summary":"Get Platform Stats","description":"Return aggregate platform statistics across all simulations.","operationId":"get_platform_stats_api_v1_admin_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/dashboard":{"get":{"tags":["admin"],"summary":"Get Admin Dashboard","description":"Comprehensive admin dashboard — users, costs, popular topics, all stats.\n\nBounded to the most recent 2,000 simulations to keep the response\nmanageable as the table grows. Each simulation row includes the\nmulti-MB ``results`` JSONB blob; an un-bounded ``select(\"*\")`` was\npulling tens of GB on a sim table that's now thousands of rows.\nThe 2,000-row window covers everything the dashboard's \"Recent\nsimulations\" + \"Daily activity (30d)\" + cost rollups need.","operationId":"get_admin_dashboard_api_v1_admin_dashboard_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/overview":{"get":{"tags":["admin"],"summary":"Get Population Overview","description":"One-shot summary of everything the Population Control tab renders.\n\nIncludes tier limits, seeder distribution, persistent archetype\ncounts, news feed coverage, and life events coverage — so the tab\nonly needs a single network call to render.","operationId":"get_population_overview_api_v1_admin_population_overview_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/tiers":{"get":{"tags":["admin"],"summary":"Get Tier Limits","description":"Return the current agent + token limits per tier.","operationId":"get_tier_limits_api_v1_admin_population_tiers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Update Tier Limits","description":"Update tier agent limits at runtime.\n\nChanges apply immediately in-process AND are persisted to the\n`population_config` Supabase table so they survive Railway worker\nrestarts and redeploys. If the Supabase save fails, the in-memory\nchange still stands and the failure is surfaced in the response\nso the operator can retry.","operationId":"update_tier_limits_api_v1_admin_population_tiers_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/TierLimitsUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/distribution":{"get":{"tags":["admin"],"summary":"Get Archetype Distribution","description":"Return the current seeder distribution + actual counts in Supabase.","operationId":"get_archetype_distribution_api_v1_admin_population_distribution_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Update Archetype Distribution","description":"Update the seeder's target distribution at runtime.\n\nOnly valid preset keys are accepted. The dict is replaced wholesale\nso operators can re-weight the swarm without editing code. Changes\nare persisted to the `population_config` Supabase table so they\nsurvive worker restarts.","operationId":"update_archetype_distribution_api_v1_admin_population_distribution_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/DistributionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/config/status":{"get":{"tags":["admin"],"summary":"Get Population Config Status","description":"Return persistence health for the Population Control tab.\n\nTells the admin UI whether the `population_config` Supabase table\nis reachable, whether it's been initialised, who edited it last,\nand when. Used to render the \"LAST SAVED\" indicator in the header.","operationId":"get_population_config_status_api_v1_admin_population_config_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/config/reload":{"post":{"tags":["admin"],"summary":"Reload Population Config","description":"Force a reload of the persistent config from Supabase.\n\nUseful when an operator edits `population_config` directly via SQL\nor when two workers have drifted out of sync. Returns the same\nstatus dict as the startup load path, so the UI can show which\nslots were applied.","operationId":"reload_population_config_api_v1_admin_population_config_reload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/news-feeds":{"get":{"tags":["admin"],"summary":"Get News Feed Registry","description":"Return the full per-preset × per-diet news feed registry.","operationId":"get_news_feed_registry_api_v1_admin_population_news_feeds_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Upsert News Feed","description":"Upsert a (preset_key, media_diet) → [urls] entry in the registry.\n\n`urls=[]` deletes the entry. Keys not in the demographic preset\nregistry are rejected. Changes are persisted to the\n`population_config` Supabase table so they survive worker restarts.","operationId":"upsert_news_feed_api_v1_admin_population_news_feeds_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/NewsFeedUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/news-feeds/{preset_key}/test":{"get":{"tags":["admin"],"summary":"Test News Feed","description":"Fetch live headlines for a (preset_key, media_diet) combo right now.\n\nAdmin sanity check — confirms the registered URLs are actually\nreachable and returning content. Cache is bypassed for the test so\noperators see the current state of the source, not stale data.","operationId":"test_news_feed_api_v1_admin_population_news_feeds__preset_key__test_get","parameters":[{"name":"preset_key","in":"path","required":true,"schema":{"type":"string","title":"Preset Key"}},{"name":"media_diet","in":"query","required":false,"schema":{"type":"string","default":"mainstream_news","title":"Media Diet"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/life-events":{"get":{"tags":["admin"],"summary":"Get Life Events Registry","description":"Return every regional + cohort life event pool for admin inspection.","operationId":"get_life_events_registry_api_v1_admin_population_life_events_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin"],"summary":"Upsert Life Events","description":"Replace the regional life-events pool for a preset.\n\nReplaces the list wholesale. Empty list deletes the preset's\nregional pool (agents fall back to cohort + global only). Changes\nare persisted to the `population_config` Supabase table so they\nsurvive worker restarts.","operationId":"upsert_life_events_api_v1_admin_population_life_events_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/LifeEventsUpsert"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/archetypes/{archetype_id}/backstory":{"post":{"tags":["admin"],"summary":"Generate Archetype Backstory Endpoint","description":"Generate a six-field cohort backstory for a single archetype and\npersist it onto the archetype's persona in Supabase.\n\nUses the cheap LLM tier — ~$0.0002 per call. The backstory is\ndeterministic on OCEAN + demographics + birth year + regional life\nevents, so regenerating produces a similar narrative.","operationId":"generate_archetype_backstory_endpoint_api_v1_admin_population_archetypes__archetype_id__backstory_post","parameters":[{"name":"archetype_id","in":"path","required":true,"schema":{"type":"string","title":"Archetype Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/archetypes/backstory/backfill":{"post":{"tags":["admin"],"summary":"Backfill Backstories Endpoint","description":"Backfill cohort backstories for archetypes that don't yet have one.\n\nFinds every persistent archetype whose `persona.backstory` is empty,\nprocesses up to `limit` of them in parallel via the cheap LLM tier,\nand writes the generated six-field backstory back to Supabase.\n\nWhy a limit: Railway's edge proxy times out long requests, so the\noperator runs this repeatedly (or from a loop) until\n`backstory_missing` hits zero. 25 concurrent cheap-tier calls\ncomplete in roughly 3-6 seconds and cost about $0.005, so the\noperator can clear a 500-archetype swarm in under a minute of\nclicking without risking a timeout.\n\nThe endpoint is idempotent and safe to retry: we re-fetch the\nmissing set on every call, so already-backfilled archetypes are\nskipped naturally.","operationId":"backfill_backstories_endpoint_api_v1_admin_population_archetypes_backstory_backfill_post","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":25,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/population/presets":{"get":{"tags":["admin"],"summary":"List Population Presets","description":"Return every demographic preset with its OCEAN + demographic config.\n\nUsed by the Population Control tab's preset editor — operators can\nclick into a preset and see every field it carries.","operationId":"list_population_presets_api_v1_admin_population_presets_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users":{"get":{"tags":["admin"],"summary":"List Users","description":"List users with plan, wallet balance, lifetime totals, sim count.\n\nNewest signups first. ``search`` does a case-insensitive email\nsubstring match (Supabase ilike). Returns an augmented view\nassembled from four tables — public.users, public.wallets,\npublic.subscriptions, public.simulations — so the admin Users\ntab can render everything in one screen.","operationId":"list_users_api_v1_admin_users_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}":{"get":{"tags":["admin"],"summary":"Get User Detail","description":"Full detail for one user: profile + wallet + subs + recent txs + recent sims.","operationId":"get_user_detail_api_v1_admin_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/wallet-adjustment":{"post":{"tags":["admin"],"summary":"Wallet Adjustment","description":"Manual wallet credit or debit by an admin.\n\nBody: ``{\"delta_cents\": int (signed), \"note\": str}``.\nPositive delta_cents credits the user; negative debits. Records to\nboth ``wallet_transactions.note`` (legacy display) and the canonical\n``admin_audit_log`` (cross-references all admin mutations).","operationId":"wallet_adjustment_api_v1_admin_users__user_id__wallet_adjustment_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/subscription":{"post":{"tags":["admin"],"summary":"Set User Subscription","description":"Manual subscription override — set or clear a user's plan + period.\n\nBody shape:\n    {\n        \"plan\": \"free\" | \"pro\" | \"enterprise\",  // \"free\" cancels any active sub\n        \"period_end\": \"2026-12-31T23:59:59Z\" | null,  // ignored when plan=free\n        \"monthly_credit_cents\": int (optional, defaults to plan default)\n    }\n\nUsed for comp accounts and one-off promotions where Stripe isn't\nin the loop. Cancels any existing active subscription and inserts\na new one with manual_<uuid> Stripe IDs (no real Stripe customer).\nThe frontend Manage Subscription button won't work for these,\nwhich is intended — comp accounts shouldn't hit the Stripe portal.","operationId":"set_user_subscription_api_v1_admin_users__user_id__subscription_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/admin-flag":{"post":{"tags":["admin"],"summary":"Set User Admin Flag","description":"Promote or demote a user's admin flag.\n\nBody: ``{\"is_admin\": bool}``. Returns 400 if the caller would\nde-admin themselves (lockout protection — there must always be at\nleast one admin who can recover).","operationId":"set_user_admin_flag_api_v1_admin_users__user_id__admin_flag_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/billing/ledger":{"get":{"tags":["admin"],"summary":"Billing Ledger","description":"Global wallet-transactions ledger. Most recent first.\n\nFilter by ``tx_type`` to focus on e.g. only topups or only\nstudy_overage rows when investigating an issue.","operationId":"billing_ledger_api_v1_admin_billing_ledger_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"Offset"}},{"name":"tx_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tx Type"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/billing/summary":{"get":{"tags":["admin"],"summary":"Billing Summary","description":"MRR estimate + wallet totals + counts for the admin dashboard.\n\nFast aggregates only; no per-user breakdown. For the full\npicture the admin drills into /admin/users or\n/admin/billing/ledger.","operationId":"billing_summary_api_v1_admin_billing_summary_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/billing/webhooks":{"get":{"tags":["admin"],"summary":"Billing Webhooks","description":"Recent Stripe webhook events — useful for forensics.","operationId":"billing_webhooks_api_v1_admin_billing_webhooks_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/keys":{"get":{"tags":["developer"],"summary":"List Api Keys","description":"List all API keys for the authenticated user.","operationId":"list_api_keys_api_v1_developer_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/KeySummary"},"type":"array","title":"Response List Api Keys Api V1 Developer Keys Get"}}}}}},"post":{"tags":["developer"],"summary":"Create Api Key","description":"Create a new API key for a user.\n\nThe key's tier is the user's *currently active* subscription\nplan (via ``resolve_active_plan``), NOT the cached ``users.plan``\ncolumn. An expired Pro sub mints a free-tier (sandbox) key.","operationId":"create_api_key_api_v1_developer_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/keys/{key_id}":{"delete":{"tags":["developer"],"summary":"Revoke Api Key","description":"Revoke (deactivate) an API key. The key remains in the DB for audit.","operationId":"revoke_api_key_api_v1_developer_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/keys/{key_id}/rotate":{"post":{"tags":["developer"],"summary":"Rotate Api Key","description":"Rotate an API key in place — same id, new secret.\n\nStripe-style rotation. Use when you suspect the secret leaked but\ndon't want to lose the key id (history, name, audit linkage). The\nold secret stops working immediately; clients still using it get\n401 from the next request onward.\n\nFor a graceful rotation with a grace period, mint a fresh key\n(POST /keys), migrate clients, then DELETE the old key. The\nrotation primitive here is deliberately abrupt — that's the\nonly safe shape when the trigger is a suspected leak.\n\nAuthentication: must use a key belonging to the user that owns\nthe target key. Self-rotating the calling key is allowed; the\nnew secret comes back in the response.","operationId":"rotate_api_key_api_v1_developer_keys__key_id__rotate_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/usage":{"get":{"tags":["developer"],"summary":"Get Usage","description":"Get monthly usage summary for all of the user's API keys.","operationId":"get_usage_api_v1_developer_usage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/UsageSummary"},"type":"array","title":"Response Get Usage Api V1 Developer Usage Get"}}}}}}},"/api/v1/developer/presets":{"get":{"tags":["developer"],"summary":"List Presets","description":"List all available demographic presets with labels and OCEAN overview.","operationId":"list_presets_api_v1_developer_presets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/developer/simulations/estimate":{"post":{"tags":["developer"],"summary":"Estimate Simulation","description":"Preview cost + duration for a hypothetical simulation.\n\nFree to call (no wallet debit, no sim row created). Use this from\nautonomous agents that need to budget before committing to a run.\nThe returned ``estimate_cents`` is the upper-bound charge —\nactual debits at settle time are typically lower because the\nmetered cost depends on real LLM token usage, not just agent count.\n\nA cheap pre-flight on this endpoint also confirms the API key is\nvalid and not over its rate limit, so agents can use it as a\nhealth check before scheduling a long-running async sim.","operationId":"estimate_simulation_api_v1_developer_simulations_estimate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/simulations":{"post":{"tags":["developer"],"summary":"Run Simulation","description":"Create and run a simulation synchronously. Returns results when complete.\n\nThis is the primary developer endpoint — a single call that creates a sim,\nruns it, and returns the full results.  For large populations (>100 agents)\nthis may take 30-120 seconds; consider using ``POST /simulations/async``\n+ polling ``GET /simulations/{id}/status`` for production workloads.\n\nPass ``Idempotency-Key: <opaque>`` header to make retries safe — a\nrepeat POST with the same key + same API key returns the original\nresponse without re-running the simulation. 24h retention.","operationId":"run_simulation_api_v1_developer_simulations_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunSimulationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/simulations/stream":{"post":{"tags":["developer"],"summary":"Stream Simulation","description":"Run a simulation as an SSE stream — events fire as agents complete.\n\nSame input shape as POST /simulations. Output is text/event-stream\ninstead of JSON. Event types:\n  - started           {mode, agent_count}\n  - agent_result      {agent: ..., result: ...}     (voting)\n  - round_start       {round_num, total_rounds}     (debate)\n  - round_complete    {round_num, summary}          (debate)\n  - progress          {completed, total, phase?}\n  - complete          {full results dict}\n  - error             {message, error_type?}\n\nWallet billing identical to the sync endpoint: reserve up-front,\nsettle on `complete`, refund on `error`. Idempotency-Key /\ncallback_url / image_url are NOT supported on this endpoint\nyet — use POST /simulations or POST /simulations/async for those.","operationId":"stream_simulation_api_v1_developer_simulations_stream_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunSimulationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/simulations/async":{"post":{"tags":["developer"],"summary":"Run Simulation Async","description":"Create a simulation and run it in the background.\n\nReturns immediately with the simulation ID and polling URLs.\nUse ``GET /simulations/{id}/status`` to check progress, then\n``GET /simulations/{id}`` to fetch results once complete.\n\nRecommended for populations > 100 agents where the sync endpoint\nwould exceed HTTP timeout limits.\n\nHonors the ``Idempotency-Key`` header — same key on retry returns\nthe original {id, poll_url, results_url} without spawning a\nduplicate background task.","operationId":"run_simulation_async_api_v1_developer_simulations_async_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunSimulationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AsyncSimulationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/simulations/{sim_id}":{"get":{"tags":["developer"],"summary":"Get Simulation","description":"Get a simulation by ID. Only returns simulations owned by the authenticated user.","operationId":"get_simulation_api_v1_developer_simulations__sim_id__get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/developer/simulations/{sim_id}/status":{"get":{"tags":["developer"],"summary":"Get Simulation Status","description":"Lightweight status check for polling (no results payload).","operationId":"get_simulation_status_api_v1_developer_simulations__sim_id__status_get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/status":{"get":{"tags":["admin-llm"],"summary":"Llm Status","description":"Full LLM status: all models, roles, API keys, availability.","operationId":"llm_status_api_v1_admin_llm_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/roles":{"get":{"tags":["admin-llm"],"summary":"List Roles","description":"List all model roles with labels and descriptions.","operationId":"list_roles_api_v1_admin_llm_roles_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/models":{"get":{"tags":["admin-llm"],"summary":"List Models","description":"List all registered models with their current status.","operationId":"list_models_api_v1_admin_llm_models_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["admin-llm"],"summary":"Create Model","description":"Add a new model to the registry.","operationId":"create_model_api_v1_admin_llm_models_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/ModelCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/models/{model_id}":{"get":{"tags":["admin-llm"],"summary":"Get Model","description":"Get a single model's details.","operationId":"get_model_api_v1_admin_llm_models__model_id__get","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","title":"Model Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["admin-llm"],"summary":"Update Model Endpoint","description":"Update a model's configuration (enable/disable, priority, roles, etc.).","operationId":"update_model_endpoint_api_v1_admin_llm_models__model_id__put","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","title":"Model Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin-llm"],"summary":"Delete Model","description":"Remove a model from the registry.","operationId":"delete_model_api_v1_admin_llm_models__model_id__delete","parameters":[{"name":"model_id","in":"path","required":true,"schema":{"type":"string","title":"Model Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/roles/{role_id}/models":{"get":{"tags":["admin-llm"],"summary":"Models For Role","description":"List models assigned to a specific role, sorted by priority.","operationId":"models_for_role_api_v1_admin_llm_roles__role_id__models_get","parameters":[{"name":"role_id","in":"path","required":true,"schema":{"type":"string","title":"Role Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/api-keys":{"get":{"tags":["admin-llm"],"summary":"List Api Keys","description":"List all tracked API keys with masked values.","operationId":"list_api_keys_api_v1_admin_llm_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/api-keys/{env_var}":{"put":{"tags":["admin-llm"],"summary":"Update Api Key","description":"Set or update an API key in the current process environment.\n\nNote: This persists for the lifetime of the worker process. For\npersistence across deploys, also set the key in Railway env vars.","operationId":"update_api_key_api_v1_admin_llm_api_keys__env_var__put","parameters":[{"name":"env_var","in":"path","required":true,"schema":{"type":"string","title":"Env Var"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin-llm"],"summary":"Delete Api Key","description":"Remove an API key from the current process environment.","operationId":"delete_api_key_api_v1_admin_llm_api_keys__env_var__delete","parameters":[{"name":"env_var","in":"path","required":true,"schema":{"type":"string","title":"Env Var"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/usage":{"get":{"tags":["admin-llm"],"summary":"Get Usage","description":"Return per-model usage stats (calls, tokens, cost, failures).","operationId":"get_usage_api_v1_admin_llm_usage_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/usage-by-task":{"get":{"tags":["admin-llm"],"summary":"Get Usage By Task","description":"Return per-(model, task) usage breakdown.\n\nTasks are the labels set via ``cost_tracker.track_as`` at each\nLLM-call entry point — e.g. ``screening_agent_vote``,\n``screening_synthesis``, ``screening_expert_report``,\n``screening_debate``, ``voting_agent``, ``debate_round``,\n``normalizer``, ``daily_cycle_reflection``. Rows without a label\nshow up as ``other`` — those call sites haven't been instrumented\nyet.\n\nAlso returns model-level totals so the UI can show both views\nwithout a second round-trip, and sorted descending by cost so the\ntop spenders are the first thing you see.","operationId":"get_usage_by_task_api_v1_admin_llm_usage_by_task_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/llm/reset":{"post":{"tags":["admin-llm"],"summary":"Reset Registry","description":"Reset all models to factory defaults. Destructive operation.","operationId":"reset_registry_api_v1_admin_llm_reset_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/topup/checkout":{"post":{"tags":["billing"],"summary":"Create Topup Checkout","description":"Create a Stripe Checkout Session for a one-off wallet top-up.","operationId":"create_topup_checkout_api_v1_billing_topup_checkout_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/TopupRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/subscribe":{"post":{"tags":["billing"],"summary":"Create Subscription Checkout","description":"Create a Stripe Checkout Session for Pro / Max subscription.","operationId":"create_subscription_checkout_api_v1_billing_subscribe_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/SubscribeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/portal":{"get":{"tags":["billing"],"summary":"Create Portal","description":"Open the Stripe Customer Portal for billing self-service.","operationId":"create_portal_api_v1_billing_portal_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/wallet":{"get":{"tags":["billing"],"summary":"Get Wallet","description":"Return the user's current wallet balance + lifetime totals + plan + recent txs.\n\nUsed by the account header widget and the billing page. Single\nround-trip so the frontend doesn't have to assemble three calls.\n\nThe three Supabase reads (wallet snapshot, ledger, plan resolution)\nare independent, so we run them in parallel via ``asyncio.gather``\nover thread-pooled sync calls. Roughly cuts response time from\nsum-of-three to max-of-three.\n\nFiltering — the ledger contains a zero-delta ``study_debit`` row\nper LLM call as internal bookkeeping; those are noise to the\ncustomer (one study can produce hundreds of rows) and historically\ncarried per-call notes that could leak model identifiers. We\ndrop them entirely before returning. Notes on the rows we keep\nare scrubbed via ``_safe_note_for_customer`` as a second layer.","operationId":"get_wallet_api_v1_billing_wallet_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/subscription":{"get":{"tags":["billing"],"summary":"Get Subscription","description":"Return the user's active subscription record if any, else ``{plan: \"free\"}``.\n\nFrontend uses this to decide whether to show the \"upgrade\" CTA\nor the \"manage subscription\" link on the billing page.","operationId":"get_subscription_api_v1_billing_subscription_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/vouchers":{"post":{"tags":["billing"],"summary":"Create Voucher Endpoint","description":"Mint a new voucher. Debits the wallet by ``amount_cents`` immediately.","operationId":"create_voucher_endpoint_api_v1_billing_vouchers_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/VoucherCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["billing"],"summary":"List Vouchers Endpoint","description":"Newest-first list of vouchers the caller has created.","operationId":"list_vouchers_endpoint_api_v1_billing_vouchers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/vouchers/{voucher_id}":{"delete":{"tags":["billing"],"summary":"Delete Voucher Endpoint","description":"Refund + soft-delete an unused voucher.","operationId":"delete_voucher_endpoint_api_v1_billing_vouchers__voucher_id__delete","parameters":[{"name":"voucher_id","in":"path","required":true,"schema":{"type":"string","title":"Voucher Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/vouchers/redeem":{"post":{"tags":["billing"],"summary":"Redeem Voucher Endpoint","description":"Redeem a voucher code. Credits the caller's wallet.","operationId":"redeem_voucher_endpoint_api_v1_billing_vouchers_redeem_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/VoucherRedeemRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/webhook":{"post":{"tags":["billing"],"summary":"Stripe Webhook","description":"Stripe webhook receiver.\n\nSignature-verified via STRIPE_WEBHOOK_SECRET. Idempotent via\nboth:\n  1. stripe_webhook_events table (primary key on stripe_event_id)\n  2. wallet_transactions.stripe_event_id unique index (for\n     events that produce a wallet debit/credit)\n\nNever returns 5xx for a known event type — Stripe retries on 5xx\nwhich can cause a re-processing storm. We return 200 for\nduplicates and unknown types; only malformed/unsigned payloads\nreturn 400.\n\nEvents handled:\n  - payment_intent.succeeded         → wallet top-up credit\n  - checkout.session.completed       → subscription activation (first time)\n  - invoice.paid                     → monthly subscription_credit\n  - customer.subscription.updated    → sync status/period\n  - customer.subscription.deleted    → mark canceled\n  - charge.dispute.created           → log (dunning in phase 5)","operationId":"stripe_webhook_api_v1_billing_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/me":{"get":{"tags":["profile"],"summary":"Get Me","description":"Return the full profile row for the authenticated user.\n\nIf the ``public.users`` row hasn't been materialized yet (the\nsignup trigger should handle this, but it's been known to fail\nsilently when the trigger's plpgsql errors), we return a minimal\nprofile built from the JWT alone so the UI still works.","operationId":"get_me_api_v1_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["profile"],"summary":"Update Me","description":"Partial update. Only fields present in the request body are written.","operationId":"update_me_api_v1_me_put","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/ProfileUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["profile"],"summary":"Delete Me","description":"Delete the authenticated user's account.\n\nCascades to all owned data via the FK constraints on:\n  - public.wallets           (ON DELETE CASCADE)\n  - public.wallet_transactions  (ON DELETE CASCADE via user_id)\n  - public.subscriptions     (ON DELETE CASCADE)\n  - public.simulations       (ON DELETE CASCADE if present)\n  - public.api_keys          (ON DELETE CASCADE if present)\n\nAlso removes the auth.users row via the Supabase admin API so the\nuser can't re-authenticate with the old credentials.\n\nThis is a hard delete by design — GDPR right-to-erasure compliance\nrequires data removal, not soft-delete. If audit retention becomes\na requirement later, swap to a dated tombstone approach here.","operationId":"delete_me_api_v1_me_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/avatar/path":{"post":{"tags":["profile"],"summary":"Mint Avatar Path","description":"Reserve a storage path the client will upload to.\n\nThe client calls this, uploads the file via the Supabase storage\nSDK (which enforces the RLS policies on storage.objects), and then\nPUTs /me with the returned ``public_url``. We don't pre-create a\nstorage row here — the upload itself creates it. This endpoint\nonly exists to centralize path conventions so the client doesn't\nhave to know about them.","operationId":"mint_avatar_path_api_v1_me_avatar_path_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/AvatarPathRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvatarPathResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/api-keys":{"get":{"tags":["profile"],"summary":"List My Api Keys","description":"List every API key owned by the authenticated user.","operationId":"list_my_api_keys_api_v1_me_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ApiKeySummary"},"title":"Response List My Api Keys Api V1 Me Api Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["profile"],"summary":"Create My Api Key","description":"Mint a fresh API key for the authenticated user.\n\nGated to paid plans (Pro / Max). Free-tier users get a 402\n\"subscription required\" so the dashboard can surface an upgrade\nCTA instead of a generic error. The tier stored on the row is\nthe user's *currently active* subscription plan via\n``resolve_active_plan`` — an expired Pro sub blocks new key\ncreation rather than minting a stale Pro key.\n\nThe raw key is returned ONCE in this response. Subsequent\nGET /me/api-keys calls return only the prefix — the hash is\none-way.","operationId":"create_my_api_key_api_v1_me_api_keys_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/ApiKeyCreateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/api-keys/{key_id}":{"delete":{"tags":["profile"],"summary":"Revoke My Api Key","description":"Mark an API key as inactive. The row stays for audit; the key\nstops authenticating on the next request.","operationId":"revoke_my_api_key_api_v1_me_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/api-keys/{key_id}/rotate":{"post":{"tags":["profile"],"summary":"Rotate My Api Key","description":"Rotate an API key in place — same id, new secret. Old secret\nstops working immediately. The raw new secret comes back ONCE\nin the response (same copy-once contract as create).","operationId":"rotate_my_api_key_api_v1_me_api_keys__key_id__rotate_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/benchmarks/public":{"get":{"tags":["benchmarks"],"summary":"Get Public Benchmarks","description":"Public validation dashboard data — the full ledger + methodology.\n\nExposed without auth. Intended as a credibility anchor linked from\nthe marketing site and in-product help.","operationId":"get_public_benchmarks_api_v1_benchmarks_public_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BenchmarkPublicResponse"}}}}}}},"/api/v1/benchmarks/public/summary":{"get":{"tags":["benchmarks"],"summary":"Get Public Benchmark Summary","description":"Lightweight version for homepage/card embeds — no per-question data.","operationId":"get_public_benchmark_summary_api_v1_benchmarks_public_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Public Benchmark Summary Api V1 Benchmarks Public Summary Get"}}}}}}},"/api/v1/ad-lab/benchmarks":{"get":{"tags":["ad-lab"],"summary":"Get Benchmarks","description":"Return the Wordstream priors used for calibration (transparency).","operationId":"get_benchmarks_api_v1_ad_lab_benchmarks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/ad-lab/upload":{"post":{"tags":["ad-lab"],"summary":"Upload Creative","description":"Stash a creative (image or video) for a later run.\n\nDelegates to the existing `services.media_store` so Ads Labo reuses the\nSupabase bucket + local-tmp fallback already wired up for Screening\nRoom uploads. Returns an upload_id the wizard attaches to the launch\nrequest.","operationId":"upload_creative_api_v1_ad_lab_upload_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_creative_api_v1_ad_lab_upload_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ad-lab":{"post":{"tags":["ad-lab"],"summary":"Create Ad Lab Run","description":"Create a new Ads Labo run and schedule it in the background.\n\nReturns immediately with `{id, status: \"pending\"}`. The client polls\n`GET /api/v1/ad-lab/{id}` to see progress and final results.\n\nOrdering matches the Pulse flow (``routers/simulations.py``): INSERT\nthe ``simulations`` row first, THEN reserve the wallet. Until May 14\nthis ran in the reverse order, which made\n``wallet_transactions.sim_id`` reference a row that didn't exist\nyet. With the FK on that column intact, every reserve failed with\n``foreign_key_violation`` → ``APIError`` → 503 \"couldn't reach your\nwallet\". The May 14 operator SQL dropped that FK; this refactor\nmatches Pulse's ordering so we don't depend on the FK being absent.","operationId":"create_ad_lab_run_api_v1_ad_lab_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/AdLabCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["ad-lab"],"summary":"List Ad Lab Runs","description":"Return the most recent Ads Labo runs, optionally filtered by user.\n\nUsed by the dashboard backfill — the local sim index only knows\nabout runs launched after the per-agent pricing redesign shipped.\nOlder Ads Labo runs need the server as the source of truth.","operationId":"list_ad_lab_runs_api_v1_ad_lab_get","parameters":[{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/ad-lab/{sim_id}":{"get":{"tags":["ad-lab"],"summary":"Get Ad Lab Run","description":"Poll status + results for an Ads Labo run.","operationId":"get_ad_lab_run_api_v1_ad_lab__sim_id__get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/audience/taxonomy":{"get":{"tags":["audience"],"summary":"Get Taxonomy","description":"Return the interest taxonomy used by audience builders.","operationId":"get_taxonomy_api_v1_audience_taxonomy_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/audience/presets":{"get":{"tags":["audience"],"summary":"Get Presets","description":"Return demographic presets available as audience starters.","operationId":"get_presets_api_v1_audience_presets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/audience/estimate-reach":{"post":{"tags":["audience"],"summary":"Estimate Reach","description":"Live preview of how many agents match the current targeting spec.\n\nCheap — generates a local persona sample and runs the pure-Python\ntargeting filter. No LLM calls. Used by audience-builder badges so\nthe user gets feedback while editing targeting.","operationId":"estimate_reach_api_v1_audience_estimate_reach_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EstimateReachRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crowd/sample":{"get":{"tags":["crowd-public"],"summary":"Crowd Sample","description":"Random, sanitized sample of the persistent archetype population.\n\nNo auth. Intended for the public marketing page that visualizes the\ncrowd. Returns sanitized cards (name, age, country, OCEAN, values,\nmedia diet, latest opinion) — no full memory log, no relationship\ninternals, no internal IDs.","operationId":"crowd_sample_api_v1_crowd_sample_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":60,"minimum":1,"default":24,"title":"Limit"}},{"name":"seed","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Optional seed for stable sampling","title":"Seed"},"description":"Optional seed for stable sampling"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Crowd Sample Api V1 Crowd Sample Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/crowd/stats":{"get":{"tags":["crowd-public"],"summary":"Crowd Stats","description":"Lightweight aggregate stats over the persistent population.\n\nDesigned for embed cards and hero strips. No auth.","operationId":"crowd_stats_api_v1_crowd_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Crowd Stats Api V1 Crowd Stats Get"}}}}}}},"/api/v1/signals/engagement":{"post":{"tags":["signals"],"summary":"Post Engagement","operationId":"post_engagement_api_v1_signals_engagement_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/EngagementBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Engagement Api V1 Signals Engagement Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/signals/outcomes":{"post":{"tags":["signals"],"summary":"Post Outcome","operationId":"post_outcome_api_v1_signals_outcomes_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/OutcomeBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Outcome Api V1 Signals Outcomes Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/signals/outcomes/{simulation_id}":{"get":{"tags":["signals"],"summary":"Get Outcome","description":"Return this user's outcome row for the given sim, or 404.","operationId":"get_outcome_api_v1_signals_outcomes__simulation_id__get","parameters":[{"name":"simulation_id","in":"path","required":true,"schema":{"type":"string","title":"Simulation Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Outcome Api V1 Signals Outcomes  Simulation Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/signals/page-view":{"post":{"tags":["signals"],"summary":"Post Page View","description":"Best-effort page-view writer.\n\nAlways returns 200 — telemetry is decoration, not correctness. A\nSupabase outage must not block the user's navigation. The\nunderlying writer logs failures and returns False, which we\nsurface as ``ok: false`` so the client can decide whether to\nretry (most won't bother).","operationId":"post_page_view_api_v1_signals_page_view_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/PageViewBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Post Page View Api V1 Signals Page View Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/cohorts":{"get":{"tags":["cohorts"],"summary":"List Cohorts","operationId":"list_cohorts_api_v1_cohorts_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response List Cohorts Api V1 Cohorts Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["cohorts"],"summary":"Create Cohort","operationId":"create_cohort_api_v1_cohorts_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/CohortCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Cohort Api V1 Cohorts Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/cohorts/{cohort_id}":{"get":{"tags":["cohorts"],"summary":"Get Cohort","operationId":"get_cohort_api_v1_cohorts__cohort_id__get","parameters":[{"name":"cohort_id","in":"path","required":true,"schema":{"type":"string","title":"Cohort Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Cohort Api V1 Cohorts  Cohort Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["cohorts"],"summary":"Update Cohort","operationId":"update_cohort_api_v1_cohorts__cohort_id__put","parameters":[{"name":"cohort_id","in":"path","required":true,"schema":{"type":"string","title":"Cohort Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CohortUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Update Cohort Api V1 Cohorts  Cohort Id  Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["cohorts"],"summary":"Delete Cohort","operationId":"delete_cohort_api_v1_cohorts__cohort_id__delete","parameters":[{"name":"cohort_id","in":"path","required":true,"schema":{"type":"string","title":"Cohort Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Cohort Api V1 Cohorts  Cohort Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/cohorts/{cohort_id}/use":{"post":{"tags":["cohorts"],"summary":"Bump Cohort Use","description":"Frontend calls this when a cohort is loaded into a launch\nwizard. Tracks adoption + drives the dashboard \"your most-used\ncohorts\" widget.","operationId":"bump_cohort_use_api_v1_cohorts__cohort_id__use_post","parameters":[{"name":"cohort_id","in":"path","required":true,"schema":{"type":"string","title":"Cohort Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Bump Cohort Use Api V1 Cohorts  Cohort Id  Use Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lineage":{"post":{"tags":["cohorts"],"summary":"Record Lineage","operationId":"record_lineage_api_v1_lineage_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/LineageCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Record Lineage Api V1 Lineage Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/lineage/{sim_id}":{"get":{"tags":["cohorts"],"summary":"Get Lineage","operationId":"get_lineage_api_v1_lineage__sim_id__get","parameters":[{"name":"sim_id","in":"path","required":true,"schema":{"type":"string","title":"Sim Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Lineage Api V1 Lineage  Sim Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/dashboard/cockpit":{"get":{"tags":["dashboard"],"summary":"Get Cockpit","operationId":"get_cockpit_api_v1_dashboard_cockpit_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Cockpit Api V1 Dashboard Cockpit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health Check","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/cost-ledger":{"get":{"summary":"Health Cost Ledger","description":"Lightweight cost-ledger persist health check.\n\nReturns 200 with ``{\"status\": \"ok\", ...}`` when the background\nflush loop is healthy. Returns 503 when consecutive_failures\nhave crossed the alert threshold OR no flush has succeeded in\nthe last 30 minutes (twice the default 120s × ~7 expected\ncycles — gives plenty of slack for transient Supabase blips).\n\nNo auth so external uptime monitors (UptimeRobot, Better Stack,\netc.) can poll this. Exposes only counters + timestamps + the\nderived alert flag — no secrets, no per-user cost data, no\nper-model breakdown.\n\nReturns:\n    200 healthy:   {\"status\": \"ok\", \"seconds_since_last_success\": N, ...}\n    200 warming:   {\"status\": \"warming\", ...} when no flush has\n                   attempted yet (just-booted state).\n    503 degraded: {\"status\": \"degraded\", \"reason\": \"...\", ...}\n                   when consecutive_failures >= threshold OR\n                   last_success_at is too stale.","operationId":"health_cost_ledger_health_cost_ledger_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health/worker-queue":{"get":{"summary":"Health Worker Queue","description":"Worker queue (RQ) health check (PR-D Phase 2).\n\nReturns counters that reveal whether the cross-worker queue is\nfunctioning. Used by:\n  * Operator dashboards (depth + oldest pending age tell us if\n    a worker stalled).\n  * UptimeRobot-style external probes (200/503 distinguishes a\n    healthy queue from a degraded one).\n\nNo auth — depth + worker count are operationally useful and\ncontain no secrets / no per-user data. The same posture as\n``/health/cost-ledger``.\n\nStatus taxonomy:\n  * ``disabled``: feature flag is off. Returns 200. The web path\n    falls through to in-process ``sim_runner.launch`` —\n    operationally fine, just not the PR-D-Phase-2 architecture.\n  * ``unavailable``: flag is on but Redis or RQ aren't reachable.\n    Returns 503. The web path falls back to in-process; users\n    still get sims, but the queue is broken until restored.\n  * ``no_workers``: queue is reachable but ``worker_count == 0``.\n    Returns 503. Pending jobs would pile up forever — alert the\n    operator to boot a worker dyno.\n  * ``backlog``: workers exist but the oldest pending job has\n    been queued for >5 minutes — worker is stuck or slow.\n    Returns 503.\n  * ``ok``: queue + workers healthy, no stale backlog. Returns 200.","operationId":"health_worker_queue_health_worker_queue_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/model-status":{"get":{"summary":"Health Models","description":"Report which LLM providers have API keys configured (no secrets exposed).","operationId":"health_models_model_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/model-test":{"get":{"summary":"Test Models Live","description":"Hit each provider with a tiny request and report results.","operationId":"test_models_live_model_test_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AdLabCreate":{"properties":{"upload_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Upload Id"},"upload_filename":{"anyOf":[{"type":"string","maxLength":512},{"type":"null"}],"title":"Upload Filename"},"upload_kind":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Upload Kind"},"campaign":{"$ref":"#/components/schemas/CampaignSpec"},"targeting":{"$ref":"#/components/schemas/AudienceSpec"},"preset_key":{"type":"string","maxLength":64,"title":"Preset Key","default":"us_general_population"},"sample_size":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Sample Size","default":200},"include_cvr":{"type":"boolean","title":"Include Cvr","default":false},"user_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"User Id"}},"type":"object","required":["campaign","targeting"],"title":"AdLabCreate"},"ApiKeyCreateRequest":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name","default":"Default"}},"type":"object","title":"ApiKeyCreateRequest"},"ApiKeyCreatedResponse":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"key_prefix":{"type":"string","title":"Key Prefix"},"name":{"type":"string","title":"Name"},"tier":{"type":"string","title":"Tier"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","key","key_prefix","name","tier","created_at"],"title":"ApiKeyCreatedResponse","description":"Shape returned by POST /me/api-keys.\n\n``key`` is the raw secret — shown ONCE and never persisted on the\nserver in plaintext. The frontend must display this immediately\nin a copy-once modal because it's unrecoverable on refresh."},"ApiKeySummary":{"properties":{"id":{"type":"string","title":"Id"},"key_prefix":{"type":"string","title":"Key Prefix"},"name":{"type":"string","title":"Name"},"tier":{"type":"string","title":"Tier"},"is_active":{"type":"boolean","title":"Is Active"},"disabled":{"type":"boolean","title":"Disabled","default":false},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","key_prefix","name","tier","is_active"],"title":"ApiKeySummary","description":"Shape returned by GET /me/api-keys."},"ApiKeyUpdate":{"properties":{"value":{"type":"string","title":"Value"}},"type":"object","required":["value"],"title":"ApiKeyUpdate"},"AsyncSimulationResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"poll_url":{"type":"string","title":"Poll Url"},"results_url":{"type":"string","title":"Results Url"}},"type":"object","required":["id","status","poll_url","results_url"],"title":"AsyncSimulationResponse"},"AudienceSpec":{"properties":{"countries":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Countries"},"regions":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Regions"},"cities":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Cities"},"age_min":{"type":"integer","maximum":120.0,"minimum":0.0,"title":"Age Min","default":18},"age_max":{"type":"integer","maximum":120.0,"minimum":0.0,"title":"Age Max","default":65},"genders":{"type":"string","maxLength":32,"title":"Genders","default":"all"},"interests":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Interests"},"interest_exclusions":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Interest Exclusions"},"income_min_tier":{"anyOf":[{"type":"integer","maximum":20.0,"minimum":0.0},{"type":"null"}],"title":"Income Min Tier"},"income_max_tier":{"anyOf":[{"type":"integer","maximum":20.0,"minimum":0.0},{"type":"null"}],"title":"Income Max Tier"}},"type":"object","title":"AudienceSpec"},"AvatarPathRequest":{"properties":{"ext":{"type":"string","maxLength":8,"minLength":1,"title":"Ext"}},"type":"object","required":["ext"],"title":"AvatarPathRequest"},"AvatarPathResponse":{"properties":{"object_path":{"type":"string","title":"Object Path"},"public_url":{"type":"string","title":"Public Url"}},"type":"object","required":["object_path","public_url"],"title":"AvatarPathResponse","description":"Returned by POST /me/avatar/path.\n\n``object_path`` is what the client uploads to via the Supabase\nstorage SDK. ``public_url`` is what we save on ``users.avatar_url``\nonce the upload succeeds."},"BenchmarkPublicResponse":{"properties":{"headline":{"additionalProperties":true,"type":"object","title":"Headline"},"latest_run":{"$ref":"#/components/schemas/BenchmarkRun"},"questions":{"items":{"$ref":"#/components/schemas/BenchmarkQuestion"},"type":"array","title":"Questions"},"trajectory":{"items":{"$ref":"#/components/schemas/BenchmarkRun"},"type":"array","title":"Trajectory"},"methodology":{"additionalProperties":true,"type":"object","title":"Methodology"},"known_limitations":{"items":{"type":"string"},"type":"array","title":"Known Limitations"}},"type":"object","required":["headline","latest_run","questions","trajectory","methodology","known_limitations"],"title":"BenchmarkPublicResponse"},"BenchmarkQuestion":{"properties":{"id":{"type":"string","title":"Id"},"topic":{"type":"string","title":"Topic"},"pew_positive":{"type":"number","title":"Pew Positive"},"pew_neutral":{"type":"number","title":"Pew Neutral"},"pew_negative":{"type":"number","title":"Pew Negative"},"synthetic_positive":{"type":"number","title":"Synthetic Positive"},"synthetic_neutral":{"type":"number","title":"Synthetic Neutral"},"synthetic_negative":{"type":"number","title":"Synthetic Negative"},"mae":{"type":"number","title":"Mae"},"parity":{"type":"number","title":"Parity"},"grade":{"type":"string","title":"Grade"},"source":{"type":"string","title":"Source"},"sample_size":{"type":"integer","title":"Sample Size"}},"type":"object","required":["id","topic","pew_positive","pew_neutral","pew_negative","synthetic_positive","synthetic_neutral","synthetic_negative","mae","parity","grade","source","sample_size"],"title":"BenchmarkQuestion"},"BenchmarkRun":{"properties":{"run_id":{"type":"string","title":"Run Id"},"date":{"type":"string","title":"Date"},"agents_per_question":{"type":"integer","title":"Agents Per Question"},"preset":{"type":"string","title":"Preset"},"mean_mae":{"type":"number","title":"Mean Mae"},"mean_parity":{"type":"number","title":"Mean Parity"},"pearson_r":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pearson R"},"overall_grade":{"type":"string","title":"Overall Grade"},"total_cost_usd":{"type":"number","title":"Total Cost Usd"},"questions_completed":{"type":"integer","title":"Questions Completed"},"questions_total":{"type":"integer","title":"Questions Total"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["run_id","date","agents_per_question","preset","mean_mae","mean_parity","overall_grade","total_cost_usd","questions_completed","questions_total"],"title":"BenchmarkRun"},"Body_upload_benchmark_source_api_v1_admin_benchmarks_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_benchmark_source_api_v1_admin_benchmarks_upload_post"},"Body_upload_creative_api_v1_ad_lab_upload_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_creative_api_v1_ad_lab_upload_post"},"Body_upload_for_comparison_api_v1_simulations_upload_for_comparison_post":{"properties":{"file":{"type":"string","contentMediaType":"application/octet-stream","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_for_comparison_api_v1_simulations_upload_for_comparison_post"},"BriefRequest":{"properties":{"topic":{"type":"string","maxLength":4000,"minLength":1,"title":"Topic"},"mode":{"type":"string","maxLength":32,"title":"Mode","default":"voting"},"language":{"type":"string","maxLength":16,"title":"Language","default":"en"},"creative_context":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}],"title":"Creative Context"},"creative_intent":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Creative Intent"}},"type":"object","required":["topic"],"title":"BriefRequest"},"CampaignSpec":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name"},"brand":{"type":"string","maxLength":200,"title":"Brand","default":""},"headline":{"type":"string","maxLength":250,"title":"Headline","default":""},"primary_text":{"type":"string","maxLength":4500,"title":"Primary Text","default":""},"cta":{"type":"string","maxLength":50,"title":"Cta","default":"Learn More"},"destination_url":{"type":"string","maxLength":2000,"title":"Destination Url","default":""},"objective":{"type":"string","maxLength":32,"title":"Objective","default":"traffic"},"placement":{"type":"string","maxLength":32,"title":"Placement","default":"meta_feed"},"vertical":{"type":"string","maxLength":64,"title":"Vertical","default":"default"}},"type":"object","title":"CampaignSpec"},"CohortCreate":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}],"title":"Description"},"spec":{"additionalProperties":true,"type":"object","title":"Spec"},"base_preset":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Base Preset"}},"type":"object","required":["name","spec"],"title":"CohortCreate"},"CohortUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":80,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":400},{"type":"null"}],"title":"Description"},"spec":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Spec"},"base_preset":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Base Preset"}},"type":"object","title":"CohortUpdate"},"CreateKeyRequest":{"properties":{"user_id":{"type":"string","maxLength":64,"minLength":1,"title":"User Id"},"name":{"type":"string","maxLength":80,"title":"Name","default":"Default"}},"type":"object","required":["user_id"],"title":"CreateKeyRequest"},"CreateKeyResponse":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"key_prefix":{"type":"string","title":"Key Prefix"},"name":{"type":"string","title":"Name"},"tier":{"type":"string","title":"Tier"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","key","key_prefix","name","tier","created_at"],"title":"CreateKeyResponse"},"DistributionUpdate":{"properties":{"distribution":{"additionalProperties":{"type":"integer"},"type":"object","title":"Distribution"}},"type":"object","required":["distribution"],"title":"DistributionUpdate"},"EngagementBody":{"properties":{"simulation_id":{"type":"string","maxLength":64,"minLength":1,"title":"Simulation Id"},"event":{"type":"string","maxLength":64,"minLength":1,"title":"Event"},"event_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Event Data"}},"type":"object","required":["simulation_id","event"],"title":"EngagementBody"},"EstimateReachRequest":{"properties":{"targeting":{"$ref":"#/components/schemas/AudienceSpec"},"preset_key":{"type":"string","maxLength":64,"title":"Preset Key","default":"us_general_population"},"sample_size":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Sample Size","default":500}},"type":"object","required":["targeting"],"title":"EstimateReachRequest"},"EstimateRequest":{"properties":{"mode":{"type":"string","maxLength":32,"title":"Mode","default":"voting"},"population_size":{"type":"integer","maximum":2000.0,"minimum":5.0,"title":"Population Size","default":20},"num_rounds":{"type":"integer","maximum":15.0,"minimum":3.0,"title":"Num Rounds","default":5}},"type":"object","title":"EstimateRequest","description":"Subset of RunSimulationRequest — just what the cost depends on."},"EstimateResponse":{"properties":{"mode":{"type":"string","title":"Mode"},"population_size":{"type":"integer","title":"Population Size"},"estimate_cents":{"type":"integer","title":"Estimate Cents"},"min_cents":{"type":"integer","title":"Min Cents"},"max_cents":{"type":"integer","title":"Max Cents"},"expected_seconds_min":{"type":"integer","title":"Expected Seconds Min"},"expected_seconds_max":{"type":"integer","title":"Expected Seconds Max"},"plan":{"type":"string","title":"Plan"},"agent_cap":{"type":"integer","title":"Agent Cap"},"within_agent_cap":{"type":"boolean","title":"Within Agent Cap"}},"type":"object","required":["mode","population_size","estimate_cents","min_cents","max_cents","expected_seconds_min","expected_seconds_max","plan","agent_cap","within_agent_cap"],"title":"EstimateResponse","description":"Cost + duration preview for a hypothetical sim.\n\nEstimates are upper bounds — the wallet reserves ``estimate_cents``\non a real run and refunds the difference at settle time. ``min_cents``\nand ``max_cents`` bracket the realistic range; ``expected_seconds_*``\nis purely informational so an agent can budget its tool-calling\ntimeout. None of these values commit anything — calling this\nendpoint is free and never debits the wallet."},"FollowUpRequest":{"properties":{"question":{"type":"string","maxLength":2000,"minLength":1,"title":"Question"}},"type":"object","required":["question"],"title":"FollowUpRequest"},"FollowUpResponse":{"properties":{"answer":{"type":"string","title":"Answer"},"token_usage":{"additionalProperties":true,"type":"object","title":"Token Usage"}},"type":"object","required":["answer","token_usage"],"title":"FollowUpResponse"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"KeySummary":{"properties":{"id":{"type":"string","title":"Id"},"key_prefix":{"type":"string","title":"Key Prefix"},"name":{"type":"string","title":"Name"},"tier":{"type":"string","title":"Tier"},"is_active":{"type":"boolean","title":"Is Active"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","key_prefix","name","tier","is_active","created_at"],"title":"KeySummary"},"LifeEventsUpsert":{"properties":{"preset_key":{"type":"string","title":"Preset Key"},"events":{"items":{"type":"string"},"type":"array","title":"Events"}},"type":"object","required":["preset_key","events"],"title":"LifeEventsUpsert"},"LineageCreate":{"properties":{"parent_simulation_id":{"type":"string","maxLength":64,"minLength":1,"title":"Parent Simulation Id"},"child_simulation_id":{"type":"string","maxLength":64,"minLength":1,"title":"Child Simulation Id"},"relation":{"type":"string","maxLength":64,"minLength":1,"title":"Relation"},"via_cta":{"type":"boolean","title":"Via Cta","default":true}},"type":"object","required":["parent_simulation_id","child_simulation_id","relation"],"title":"LineageCreate"},"ModelCreate":{"properties":{"id":{"type":"string","title":"Id"},"display_name":{"type":"string","title":"Display Name"},"provider":{"type":"string","title":"Provider"},"env_var":{"type":"string","title":"Env Var"},"roles":{"items":{"type":"string"},"type":"array","title":"Roles"},"enabled":{"type":"boolean","title":"Enabled","default":true},"priority":{"type":"integer","title":"Priority","default":10},"input_cost_per_m":{"type":"number","title":"Input Cost Per M","default":0.0},"output_cost_per_m":{"type":"number","title":"Output Cost Per M","default":0.0},"max_tokens":{"type":"integer","title":"Max Tokens","default":4096},"supports_vision":{"type":"boolean","title":"Supports Vision","default":false},"supports_video":{"type":"boolean","title":"Supports Video","default":false},"notes":{"type":"string","title":"Notes","default":""}},"type":"object","required":["id","display_name","provider","env_var","roles"],"title":"ModelCreate"},"ModelUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"priority":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Priority"},"roles":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Roles"},"input_cost_per_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Input Cost Per M"},"output_cost_per_m":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Output Cost Per M"},"max_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Tokens"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","title":"ModelUpdate"},"NewsFeedUpsert":{"properties":{"preset_key":{"type":"string","title":"Preset Key"},"media_diet":{"type":"string","title":"Media Diet"},"urls":{"items":{"type":"string"},"type":"array","title":"Urls"}},"type":"object","required":["preset_key","media_diet","urls"],"title":"NewsFeedUpsert"},"NormalizeRequest":{"properties":{"topic":{"type":"string","maxLength":4000,"minLength":1,"title":"Topic"},"mode":{"type":"string","maxLength":32,"title":"Mode","default":"voting"},"language":{"type":"string","maxLength":16,"title":"Language","default":"en"}},"type":"object","required":["topic"],"title":"NormalizeRequest"},"OutcomeBody":{"properties":{"simulation_id":{"type":"string","maxLength":64,"minLength":1,"title":"Simulation Id"},"accuracy_rating":{"anyOf":[{"type":"integer","maximum":5.0,"minimum":1.0},{"type":"null"}],"title":"Accuracy Rating"},"ground_truth":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Ground Truth"},"ground_truth_data":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Ground Truth Data"},"notes":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Notes"}},"type":"object","required":["simulation_id"],"title":"OutcomeBody"},"PageViewBody":{"properties":{"path":{"type":"string","maxLength":200,"minLength":1,"title":"Path"},"referrer":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Referrer"},"session_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Session Id"}},"type":"object","required":["path"],"title":"PageViewBody"},"ProfileResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"is_admin":{"type":"boolean","title":"Is Admin","default":false},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["user_id"],"title":"ProfileResponse","description":"Shape returned by GET /me. Consumed by account + profile pages."},"ProfileUpdateRequest":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Avatar Url"}},"type":"object","title":"ProfileUpdateRequest","description":"Partial update — only fields present in the JSON are changed.\n\nEmail is NOT here; changing email requires Supabase's email-\nconfirmation flow on the client. Password is NOT here for the\nsame reason."},"RunSimulationRequest":{"properties":{"topic":{"type":"string","maxLength":4000,"minLength":1,"title":"Topic"},"mode":{"type":"string","maxLength":32,"title":"Mode","default":"voting"},"demographic_preset":{"type":"string","maxLength":64,"title":"Demographic Preset","default":"us_general_population"},"population_size":{"type":"integer","maximum":2000.0,"minimum":5.0,"title":"Population Size","default":20},"language":{"type":"string","maxLength":16,"title":"Language","default":"en"},"stance_statement":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Stance Statement"},"num_rounds":{"type":"integer","maximum":15.0,"minimum":3.0,"title":"Num Rounds","default":5},"options":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":4,"minItems":2},{"type":"null"}],"title":"Options"},"image_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Image Url"},"callback_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Callback Url"},"custom_audience":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Audience"}},"type":"object","required":["topic"],"title":"RunSimulationRequest"},"ScreeningChatRequest":{"properties":{"question":{"type":"string","maxLength":1000,"minLength":1,"title":"Question"}},"type":"object","required":["question"],"title":"ScreeningChatRequest"},"SimulationConfig":{"properties":{"population_size":{"type":"integer","title":"Population Size","default":20},"rounds":{"type":"integer","title":"Rounds","default":5}},"type":"object","title":"SimulationConfig"},"SimulationCreate":{"properties":{"mode":{"type":"string","maxLength":32,"title":"Mode","default":"voting"},"topic":{"type":"string","maxLength":4000,"minLength":1,"title":"Topic"},"config":{"$ref":"#/components/schemas/SimulationConfig","default":{"population_size":20,"rounds":5}},"num_rounds":{"type":"integer","maximum":15.0,"minimum":3.0,"title":"Num Rounds","default":5},"context_url":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Context Url"},"context_file_text":{"anyOf":[{"type":"string","maxLength":65536},{"type":"null"}],"title":"Context File Text"},"demographic_preset":{"type":"string","maxLength":64,"title":"Demographic Preset","default":"us_general_population"},"options":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Options"},"user_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"User Id"},"language":{"type":"string","maxLength":16,"title":"Language","default":"en"},"custom_audience":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Custom Audience"},"stance_statement":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Stance Statement"},"comparison_tier":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Comparison Tier"},"session_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Session Name"},"research_brief":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Research Brief"}},"type":"object","required":["topic"],"title":"SimulationCreate"},"SimulationResponse":{"properties":{"id":{"type":"string","title":"Id"},"status":{"type":"string","title":"Status"},"mode":{"type":"string","title":"Mode"},"topic":{"type":"string","title":"Topic"},"demographic_preset":{"type":"string","title":"Demographic Preset"},"population_size":{"type":"integer","title":"Population Size"},"results":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Results"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","status","mode","topic","demographic_preset","population_size"],"title":"SimulationResponse"},"SubscribeRequest":{"properties":{"plan":{"type":"string","enum":["pro","max","enterprise"],"title":"Plan"}},"type":"object","required":["plan"],"title":"SubscribeRequest"},"TierLimitsUpdate":{"properties":{"free":{"type":"integer","maximum":2000.0,"minimum":1.0,"title":"Free"},"starter":{"type":"integer","maximum":2000.0,"minimum":1.0,"title":"Starter"},"pro":{"type":"integer","maximum":2000.0,"minimum":1.0,"title":"Pro"},"max":{"anyOf":[{"type":"integer","maximum":5000.0,"minimum":1.0},{"type":"null"}],"title":"Max"},"enterprise":{"anyOf":[{"type":"integer","maximum":5000.0,"minimum":1.0},{"type":"null"}],"title":"Enterprise"}},"type":"object","required":["free","starter","pro"],"title":"TierLimitsUpdate"},"TopupRequest":{"properties":{"amount_cents":{"type":"integer","maximum":1000000.0,"minimum":500.0,"title":"Amount Cents","description":"Amount to top up, in USD cents. Min 500 ($5), max 1,000,000 ($10,000)."}},"type":"object","required":["amount_cents"],"title":"TopupRequest"},"UsageSummary":{"properties":{"month":{"type":"string","title":"Month"},"simulation_count":{"type":"integer","title":"Simulation Count"},"token_count":{"type":"integer","title":"Token Count"},"cost_usd":{"type":"number","title":"Cost Usd"}},"type":"object","required":["month","simulation_count","token_count","cost_usd"],"title":"UsageSummary"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"ValidationRequest":{"properties":{"real_distribution":{"additionalProperties":{"type":"number"},"type":"object","title":"Real Distribution"},"real_source":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Real Source"},"real_sample_size":{"anyOf":[{"type":"integer","maximum":10000000.0,"minimum":0.0},{"type":"null"}],"title":"Real Sample Size"}},"type":"object","required":["real_distribution"],"title":"ValidationRequest","description":"Body for ``POST /api/v1/simulations/{sim_id}/validate``.\n\n``real_distribution`` may carry either percentages summing to 100\nor raw respondent counts; the validator normalizes either form.\nOnly the ``positive`` / ``neutral`` / ``negative`` keys are read."},"VoucherCreateRequest":{"properties":{"amount_cents":{"type":"integer","title":"Amount Cents","description":"Voucher denomination, in USD cents. Must be one of $5/$10/$25/$50/$100."},"note":{"anyOf":[{"type":"string","maxLength":240},{"type":"null"}],"title":"Note","description":"Optional gift note shown on the creator's voucher list."}},"type":"object","required":["amount_cents"],"title":"VoucherCreateRequest"},"VoucherRedeemRequest":{"properties":{"code":{"type":"string","maxLength":40,"minLength":4,"title":"Code"}},"type":"object","required":["code"],"title":"VoucherRedeemRequest"}}}}