{"openapi":"3.1.0","info":{"title":"菜场 · Research Market (Vastcape Research API)","version":"1.0.0","description":"Read-only API for **菜场 · Research Market** — the AI Stock Research Institute's published research, track record, and knowledge.\n\nKeys are **operator-minted** for now: request one (scopes are listed per endpoint). All endpoints (except `/api/v1/meta`) require an `X-API-Key` header; each declares the scope(s) it needs; keys can hold multiple.\n\nStart at `GET /api/v1/latest` (today's daily, this week's reports, the Sharpe leaderboard — no date needed). Markdown endpoints return `text/markdown; charset=utf-8` and are cacheable. JSON responses are stable: backwards-incompatible changes ship as `/api/v2/*`.","contact":{"name":"菜场 · Research Market"}},"servers":[{"url":"https://ai-institute-worker.dmquant.workers.dev","description":"Production"},{"url":"http://127.0.0.1:8787","description":"Local dev"}],"components":{"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"Institute API key prefixed with `sk_ins_`. Issue from the admin UI or via `POST /api/v1/admin/keys`. Scopes listed under each route."}},"schemas":{"MetaResponse":{"type":"object","properties":{"project":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"color":{"type":"string"}},"required":["name"]},"search_enabled":{"type":"boolean"},"storage":{"type":"object","properties":{"d1":{"type":"boolean"},"kv":{"type":"boolean"},"r2":{"type":"boolean"},"vectorize":{"type":"boolean"}},"required":["d1","kv","r2","vectorize"]},"api_version":{"type":"string","example":"1.0.0"}},"required":["project","search_enabled","storage","api_version"]},"ListAnalystsResponse":{"type":"object","properties":{"analysts":{"type":"array","items":{"$ref":"#/components/schemas/AnalystSummary"}},"count":{"type":"integer"}},"required":["analysts","count"]},"AnalystSummary":{"type":"object","properties":{"id":{"type":"string","example":"tmt-analyst"},"name":{"type":"string","example":"TMT行业分析师"},"name_en":{"type":"string","nullable":true,"example":"TMT Analyst"},"icon":{"type":"string","nullable":true},"agent":{"type":"string","nullable":true,"example":"claude"},"specialty":{"type":"string","nullable":true},"specialty_en":{"type":"string","nullable":true},"category_id":{"type":"string","nullable":true},"category_name":{"type":"string","nullable":true},"category_name_en":{"type":"string","nullable":true},"tasks_default":{"type":"array","items":{"type":"string"}}},"required":["id","name","name_en","icon","agent","specialty","specialty_en","category_id","category_name","category_name_en","tasks_default"]},"ErrorResponse":{"type":"object","properties":{"error":{"type":"string","example":"missing scope: whiteboard:read"},"required":{"type":"string"},"granted":{"type":"array","items":{"type":"string"}}},"required":["error"]},"AnalystDetail":{"allOf":[{"$ref":"#/components/schemas/AnalystSummary"},{"type":"object","properties":{"category":{"type":"object","nullable":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"name_en":{"type":"string","nullable":true}},"required":["id","name","name_en"]}},"required":["category"]}]},"ListThreadsResponse":{"type":"object","properties":{"threads":{"type":"array","items":{"$ref":"#/components/schemas/ThreadSummary"}},"count":{"type":"integer"}},"required":["threads","count"]},"ThreadSummary":{"type":"object","properties":{"board_id":{"type":"string","format":"uuid","example":"9f360d1f-57da-463a-afb0-8fc677772abd"},"work_date":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-05-04"},"root_analyst_id":{"type":"string","nullable":true,"example":"tmt-analyst"},"root_topic":{"type":"string","nullable":true,"example":"Q1 2026 hyperscaler AI capex re-acceleration"},"root_question":{"type":"string","nullable":true},"status":{"$ref":"#/components/schemas/ThreadStatus"},"card_count":{"type":"integer","minimum":0,"example":6},"max_cards":{"type":"integer","minimum":0,"example":8},"created_at":{"type":"integer","example":1746374400000},"updated_at":{"type":"integer","example":1746392400000}},"required":["board_id","work_date","root_analyst_id","root_topic","root_question","status","card_count","max_cards","created_at","updated_at"]},"ThreadStatus":{"type":"string","enum":["queued","active","running","completed","failed"]},"ThreadDetail":{"allOf":[{"$ref":"#/components/schemas/ThreadSummary"},{"type":"object","properties":{"cards":{"type":"array","items":{"$ref":"#/components/schemas/CardSummary"}}},"required":["cards"]}]},"CardSummary":{"type":"object","properties":{"card_id":{"type":"string"},"board_id":{"type":"string"},"card_index":{"type":"integer"},"analyst_id":{"type":"string","nullable":true},"hand":{"type":"string","nullable":true,"example":"claude"},"topic":{"type":"string","nullable":true},"question":{"type":"string","nullable":true},"stance":{"type":"string","nullable":true},"status":{"type":"string"},"work_date":{"type":"string","nullable":true},"summary":{"type":"string","nullable":true},"report_path":{"type":"string","nullable":true},"report_r2_key":{"type":"string","nullable":true},"report_url":{"type":"string","nullable":true,"example":"/api/v1/whiteboard/cards/abc123/report"},"started_at":{"type":"integer","nullable":true},"completed_at":{"type":"integer","nullable":true},"follow_up_analyst_id":{"type":"string","nullable":true},"follow_up_topic":{"type":"string","nullable":true}},"required":["card_id","board_id","card_index","analyst_id","hand","topic","question","stance","status","work_date","summary","report_path","report_r2_key","report_url","started_at","completed_at","follow_up_analyst_id","follow_up_topic"]},"WeeklyIndex":{"type":"object","properties":{"week_start":{"type":"string"},"week_end":{"type":"string"},"count":{"type":"integer"},"authors":{"type":"array","items":{"$ref":"#/components/schemas/WeeklyAuthor"}}},"required":["week_start","week_end","count","authors"]},"WeeklyAuthor":{"type":"object","properties":{"analyst_id":{"type":"string"},"work_date":{"type":"string"},"languages":{"type":"array","items":{"type":"string","enum":["en","zh","plain"]}},"report_url":{"type":"string"}},"required":["analyst_id","work_date","languages","report_url"]},"PortfolioMetrics":{"type":"object","properties":{"portfolio_id":{"type":"string"},"n_snapshots":{"type":"integer"},"n_returns":{"type":"integer"},"first_at":{"type":"number","nullable":true},"last_at":{"type":"number","nullable":true},"start_nav":{"type":"number","nullable":true},"latest_nav":{"type":"number","nullable":true},"total_return_pct":{"type":"number","nullable":true},"annualized_return_pct":{"type":"number","nullable":true},"annualized_vol_pct":{"type":"number","nullable":true},"sharpe":{"type":"number","nullable":true},"sortino":{"type":"number","nullable":true},"max_drawdown_pct":{"type":"number","nullable":true},"current_drawdown_pct":{"type":"number","nullable":true},"calmar":{"type":"number","nullable":true},"best_day_pct":{"type":"number","nullable":true},"worst_day_pct":{"type":"number","nullable":true},"benchmark_id":{"type":"string","nullable":true},"benchmark_return_pct":{"type":"number","nullable":true},"alpha_pct":{"type":"number","nullable":true}},"required":["portfolio_id","n_snapshots","n_returns","first_at","last_at","start_nav","latest_nav","total_return_pct","annualized_return_pct","annualized_vol_pct","sharpe","sortino","max_drawdown_pct","current_drawdown_pct","calmar","best_day_pct","worst_day_pct","benchmark_id","benchmark_return_pct","alpha_pct"]},"LatestIndex":{"type":"object","properties":{"generated_at":{"type":"integer"},"work_date":{"type":"string"},"daily":{"type":"object","nullable":true,"properties":{"work_date":{"type":"string"},"report_url":{"type":"string"}},"required":["work_date","report_url"]},"weekly":{"type":"object","properties":{"week_start":{"type":"string"},"week_end":{"type":"string"},"count":{"type":"integer"}},"required":["week_start","week_end","count"]},"leaderboard":{"type":"array","items":{"type":"object","properties":{"portfolio_id":{"type":"string"},"sharpe":{"type":"number","nullable":true},"total_return_pct":{"type":"number","nullable":true},"alpha_pct":{"type":"number","nullable":true},"latest_nav":{"type":"number","nullable":true}},"required":["portfolio_id","sharpe","total_return_pct","alpha_pct","latest_nav"]}}},"required":["generated_at","work_date","daily","weekly","leaderboard"]},"SearchResponse":{"type":"object","properties":{"query":{"type":"string"},"search_ready":{"type":"boolean"},"results":{"type":"array","items":{"$ref":"#/components/schemas/SearchMatch"}}},"required":["query","search_ready","results"]},"SearchMatch":{"type":"object","properties":{"score":{"type":"number"},"session_id":{"type":"string","nullable":true},"path":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"excerpt":{"type":"string","nullable":true},"archived_at":{"type":"integer","nullable":true}},"required":["score","session_id","path","title","excerpt","archived_at"]},"SearchRequest":{"type":"object","properties":{"query":{"type":"string","minLength":1,"maxLength":500,"example":"AI capex hyperscaler ROI Q1 2026"},"analyst_id":{"type":"string","example":"tmt-analyst"},"session_id":{"type":"string"},"top_k":{"type":"integer","minimum":1,"maximum":12,"default":8}},"required":["query"]},"TopicPoolResponse":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/TopicPoolEntry"}},"count":{"type":"integer"}},"required":["entries","count"]},"TopicPoolEntry":{"type":"object","properties":{"pool_id":{"type":"string"},"topic":{"type":"string"},"question":{"type":"string","nullable":true},"suggested_analyst_id":{"type":"string","nullable":true},"rationale":{"type":"string","nullable":true},"source_kind":{"type":"string","example":"model_seed"},"source_run_id":{"type":"string","nullable":true},"source_analyst_id":{"type":"string","nullable":true},"priority":{"type":"string"},"score":{"type":"number"},"status":{"type":"string","enum":["candidate","promoted","expired","rejected"]},"promoted_board_id":{"type":"string","nullable":true},"created_at":{"type":"integer"},"promoted_at":{"type":"integer","nullable":true},"expires_at":{"type":"integer"}},"required":["pool_id","topic","question","suggested_analyst_id","rationale","source_kind","source_run_id","source_analyst_id","priority","score","status","promoted_board_id","created_at","promoted_at","expires_at"]},"SimilarityLogResponse":{"type":"object","properties":{"entries":{"type":"array","items":{"$ref":"#/components/schemas/SimilarityLogEntry"}},"count":{"type":"integer"},"summary_7d":{"type":"object","additionalProperties":{"type":"number"},"description":"Decision counts over the trailing 7 days, even when `limit` caps the row list. Keys are the same enum values as `decision`."}},"required":["entries","count","summary_7d"]},"SimilarityLogEntry":{"type":"object","properties":{"log_id":{"type":"string"},"decided_at":{"type":"integer","description":"Unix epoch seconds when the gate decided."},"pool_id":{"type":"string","nullable":true},"board_id":{"type":"string","nullable":true},"candidate_topic":{"type":"string","nullable":true},"candidate_question":{"type":"string","nullable":true},"decision":{"$ref":"#/components/schemas/SimilarityDecision"},"reason":{"type":"string","nullable":true},"top_cosine":{"type":"number","nullable":true},"top_match_kind":{"type":"string","nullable":true},"top_match_id":{"type":"string","nullable":true},"top_match_age_days":{"type":"number","nullable":true},"matches":{"type":"array","items":{"$ref":"#/components/schemas/SimilarityMatch"}},"thresholds":{"type":"object","nullable":true,"additionalProperties":{"nullable":true}}},"required":["log_id","decided_at","pool_id","board_id","candidate_topic","candidate_question","decision","reason","top_cosine","top_match_kind","top_match_id","top_match_age_days","matches","thresholds"]},"SimilarityDecision":{"type":"string","enum":["skip-summarize","context-augmented","fresh"]},"SimilarityMatch":{"type":"object","properties":{"score":{"type":"number"},"kind":{"type":"string","nullable":true},"id":{"type":"string","nullable":true},"age_days":{"type":"number","nullable":true},"topic":{"type":"string","nullable":true}},"required":["score","kind","id","age_days","topic"]},"ListFactCardsResponse":{"type":"object","properties":{"fact_cards":{"type":"array","items":{"$ref":"#/components/schemas/FactCard"}},"count":{"type":"integer"},"next_cursor":{"type":"string","nullable":true}},"required":["fact_cards","count","next_cursor"]},"FactCard":{"type":"object","properties":{"fact_card_id":{"type":"string"},"parent_kind":{"$ref":"#/components/schemas/FactCardParentKind"},"parent_id":{"type":"string"},"parent_session_id":{"type":"string","nullable":true},"claim_text":{"type":"string"},"claim_text_en":{"type":"string"},"claim_category":{"$ref":"#/components/schemas/ClaimCategory"},"priority":{"type":"string","enum":["high","normal","low"]},"status":{"$ref":"#/components/schemas/FactCardStatus"},"verdict":{"$ref":"#/components/schemas/Verdict"},"found_text":{"type":"string","nullable":true},"found_text_en":{"type":"string","nullable":true},"source_urls":{"type":"array","nullable":true,"items":{"type":"string"}},"similar_to_fact_id":{"type":"string","nullable":true},"similarity_score":{"type":"number","nullable":true},"embedding_status":{"type":"string","enum":["pending","done","failed","skipped"]},"created_at":{"type":"integer"},"completed_at":{"type":"integer","nullable":true},"updated_at":{"type":"integer"}},"required":["fact_card_id","parent_kind","parent_id","parent_session_id","claim_text","claim_text_en","claim_category","priority","status","verdict","found_text","found_text_en","source_urls","similar_to_fact_id","similarity_score","embedding_status","created_at","completed_at","updated_at"]},"FactCardParentKind":{"type":"string","enum":["whiteboard_card","session_message","external"]},"ClaimCategory":{"type":"string","enum":["comparison","date","financial","geographic","named_entity","numerical","prose","spec"]},"FactCardStatus":{"type":"string","enum":["queued","running","completed","failed","reused","skipped"]},"Verdict":{"type":"string","nullable":true,"enum":["verified","disputed","uncertain","unverifiable",null]},"SimilarFactsResponse":{"type":"object","properties":{"fact_card_id":{"type":"string"},"count":{"type":"integer"},"matches":{"type":"array","items":{"$ref":"#/components/schemas/SimilarFactMatch"}},"reuse_policy":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/ReusePolicy"},"description":"Per-category reuse policy (cosine threshold + TTL in days) applied when the verifier decides whether a similar prior claim's verdict can be inherited."}},"required":["fact_card_id","count","matches","reuse_policy"]},"SimilarFactMatch":{"type":"object","properties":{"fact_card_id":{"type":"string"},"score":{"type":"number"},"claim_category":{"allOf":[{"$ref":"#/components/schemas/ClaimCategory"},{"nullable":true}]},"verdict":{"$ref":"#/components/schemas/Verdict"},"claim_text_en":{"type":"string","nullable":true},"completed_at":{"type":"integer","nullable":true}},"required":["fact_card_id","score","claim_category","verdict","claim_text_en","completed_at"]},"ReusePolicy":{"type":"object","properties":{"threshold":{"type":"number"},"ttlDays":{"type":"integer"}},"required":["threshold","ttlDays"]},"SharedDataTopicsResponse":{"type":"object","properties":{"date":{"type":"string"},"available":{"type":"array","items":{"type":"object","properties":{"topic":{"type":"string"},"bytes":{"type":"integer"},"fetched_at":{"type":"string","nullable":true}},"required":["topic","bytes","fetched_at"]}},"known_topics":{"type":"array","items":{"type":"string"}}},"required":["date","available","known_topics"]}},"parameters":{}},"paths":{"/api/v1/meta":{"get":{"tags":["Meta"],"summary":"Project metadata","description":"Public endpoint. No auth required. Returns the project name, search-readiness flag, and storage-binding availability so clients can probe the API before authenticating.","responses":{"200":{"description":"Project metadata.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MetaResponse"}}}}}}},"/api/v1/analysts":{"get":{"tags":["Analysts"],"summary":"List all analysts","description":"Returns the full analyst catalog (chief economist, sector analysts, strategists, etc.) drawn from the merged profiles.json + runtime overlay. Categories are flattened — every row carries its `category_id` so clients can re-group as needed.","security":[{"ApiKey":["analysts:read"]}],"responses":{"200":{"description":"Flat list of analysts.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAnalystsResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the analysts:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/analysts/{analystId}":{"get":{"tags":["Analysts"],"summary":"Get a single analyst","description":"Returns the same shape as the list endpoint, with an additional `category` block. 404 if the ID is unknown.","security":[{"ApiKey":["analysts:read"]}],"parameters":[{"schema":{"type":"string","example":"tmt-analyst"},"required":true,"name":"analystId","in":"path"}],"responses":{"200":{"description":"Analyst record.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalystDetail"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the analysts:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown analyst ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/whiteboard/threads":{"get":{"tags":["Whiteboard"],"summary":"List recent whiteboard threads","description":"Returns the most recently updated whiteboard threads, optionally filtered by status. Use `/api/v1/whiteboard/threads/{boardId}` for the full card list.","security":[{"ApiKey":["whiteboard:read"]}],"parameters":[{"schema":{"$ref":"#/components/schemas/ThreadStatus"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50,"example":50},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"List of recent threads, newest first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the whiteboard:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/whiteboard/threads/{boardId}":{"get":{"tags":["Whiteboard"],"summary":"Get a thread with its cards","description":"Returns the thread plus all cards in order. Each card includes a `report_url` pointing to the markdown read endpoint for that card.","security":[{"ApiKey":["whiteboard:read"]}],"parameters":[{"schema":{"type":"string","example":"9f360d1f-57da-463a-afb0-8fc677772abd"},"required":true,"name":"boardId","in":"path"}],"responses":{"200":{"description":"Thread + ordered card list.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadDetail"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the whiteboard:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown board ID.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/whiteboard/cards/{cardId}/report":{"get":{"tags":["Whiteboard"],"summary":"Read a card's report markdown","description":"Returns the card's report markdown straight from R2. Most cards ship a paired EN/ZH set — pass `?lang=en` or `?lang=zh` to pick. Default tries `report.en.md` first, then falls back to the language-agnostic `report.md`.\n\nResponse is `text/markdown; charset=utf-8`.","security":[{"ApiKey":["whiteboard:read"]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"cardId","in":"path"},{"schema":{"type":"string","enum":["en","zh"],"example":"en"},"required":false,"name":"lang","in":"query"}],"responses":{"200":{"description":"Report markdown.","content":{"text/markdown":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the whiteboard:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Card or report not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/fleet-health/latest":{"get":{"tags":["Reports"],"summary":"Latest fleet-health snapshot (markdown)","description":"Returns the most recent L1 fleet-health snapshot — the same markdown the institute diagnostician reads each morning. Generated daily at 00:00 SG (16:00 UTC) and on demand. Response is `text/markdown; charset=utf-8`. Latest-snapshot is mutable (it shifts every day) so the response uses `Cache-Control: no-store` and skips ETag — re-fetch each call.","security":[{"ApiKey":["reports:read"]}],"responses":{"200":{"description":"Snapshot markdown.","content":{"text/markdown":{"schema":{"type":"string"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No snapshot generated yet.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/daily/{date}":{"get":{"tags":["Reports"],"summary":"Editorial daily report (markdown)","description":"Returns the canonical editorial daily report for the given SG work-date — written by the `daily-report-editor` analyst as `06_每日日报.{lang}.md`. Tie-breaker when multiple drafts exist: latest `archived_at` wins. Conditional GET supported via `If-None-Match` and `If-Modified-Since` (304 path skips body bytes).","security":[{"ApiKey":["reports:read"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","example":"2026-05-04"},"required":true,"name":"date","in":"path"},{"schema":{"type":"string","enum":["en","zh"]},"required":false,"name":"lang","in":"query"}],"responses":{"200":{"description":"Daily report markdown.","content":{"text/markdown":{"schema":{"type":"string"}}}},"304":{"description":"Cached copy is current."},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"No daily report published for that date.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/weekly/{date}":{"get":{"tags":["Reports"],"summary":"Weekly report index for the ISO week containing {date}","description":"Returns every analyst that published `03_weekly_report.*.md` in the Mon→Sun ISO week containing the provided date. The institute does not produce a single canonical weekly (each desk publishes its own), so this endpoint is an *index*; fetch a specific author's report via `/api/v1/reports/weekly/{date}/{analystId}`.","security":[{"ApiKey":["reports:read"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"date","in":"path"}],"responses":{"200":{"description":"Index of weekly reports.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WeeklyIndex"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/reports/weekly/{date}/{analystId}":{"get":{"tags":["Reports"],"summary":"One analyst's weekly report (markdown)","description":"Returns the analyst's `03_weekly_report.{lang}.md` for the ISO week containing `date`. Picks the most recently archived copy in the week — re-runs and partial drafts are automatically skipped in favour of the final. Conditional GET supported.","security":[{"ApiKey":["reports:read"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"date","in":"path"},{"schema":{"type":"string"},"required":true,"name":"analystId","in":"path"},{"schema":{"type":"string","enum":["en","zh"]},"required":false,"name":"lang","in":"query"}],"responses":{"200":{"description":"Weekly report markdown.","content":{"text/markdown":{"schema":{"type":"string"}}}},"304":{"description":"Cached copy is current."},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Analyst did not publish a weekly report in that week.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/portfolios/leaderboard":{"get":{"tags":["Track record"],"summary":"Risk-adjusted portfolio leaderboard (Sharpe-sorted)","description":"The institute's 26 persona-owned paper portfolios ranked by risk-adjusted return — Sharpe / Sortino / annualized vol / max-drawdown / Calmar over the daily NAV series, plus benchmark-alpha (vs CSI300/SPX) once the benchmark series has accrued. The track-record / proof-of-skill surface.","security":[{"ApiKey":["portfolios:read"]}],"responses":{"200":{"description":"Leaderboard.","content":{"application/json":{"schema":{"type":"object","properties":{"leaderboard":{"type":"array","items":{"$ref":"#/components/schemas/PortfolioMetrics"}},"count":{"type":"integer"}},"required":["leaderboard","count"]}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the portfolios:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/portfolios/{portfolioId}/metrics":{"get":{"tags":["Track record"],"summary":"Risk-adjusted metrics for one portfolio","description":"Sharpe / Sortino / vol / max-drawdown / Calmar / benchmark-alpha for a single persona portfolio.","security":[{"ApiKey":["portfolios:read"]}],"parameters":[{"schema":{"type":"string","example":"chief-strategist-cross"},"required":true,"name":"portfolioId","in":"path"}],"responses":{"200":{"description":"Metrics.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortfolioMetrics"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the portfolios:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/latest":{"get":{"tags":["Reports"],"summary":"Date-free index — today's daily, this week's reports, the leaderboard","description":"One call to find the freshest published work without knowing the date: the most recent daily report (walks back up to 3 days), the count of this week's per-desk weekly reports, and the top-5 Sharpe leaderboard. The storefront landing data.","security":[{"ApiKey":["reports:read"]}],"responses":{"200":{"description":"Latest index.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LatestIndex"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/search":{"post":{"tags":["Search"],"summary":"Semantic search over archived institute output","description":"Embeds the query with `@cf/baai/bge-m3` and returns the top-K cosine-similarity matches from the institute Vectorize index. Filter by `analyst_id` or `session_id` to scope. `search_ready: false` means the AI/Vectorize bindings aren't configured for this worker.","security":[{"ApiKey":["search:read"]}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchRequest"}}}},"responses":{"200":{"description":"Top-K matches, highest score first.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"400":{"description":"Invalid request body.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the search:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/whiteboard/topic-pool":{"get":{"tags":["Whiteboard"],"summary":"List whiteboard kickoff candidates","description":"Returns candidates queued for the hourly whiteboard kickoff cron — research topics the institute *plans* to spin a thread up on. `score` is the priority weight; the cron drains the highest-scoring `candidate` row each hour. Filter by `status` to see only queued (`candidate`) or already-launched (`promoted`) rows.","security":[{"ApiKey":["whiteboard:read"]}],"parameters":[{"schema":{"type":"string","enum":["candidate","promoted","all"],"default":"all"},"required":false,"name":"status","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Pool entries.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopicPoolResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the whiteboard:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/whiteboard/similarity-log":{"get":{"tags":["Whiteboard"],"summary":"Whiteboard similarity-gate decision log","description":"The similarity gate runs in dry-run mode before each whiteboard kickoff: it embeds the candidate topic, looks for prior threads near it, and decides whether to launch fresh (no overlap), context-augment (some overlap, augment the prompt), or skip-summarize (near-duplicate, summarize prior instead). This endpoint surfaces those decisions so you can see *why* a candidate was promoted or skipped. `summary_7d` rolls up counts across the trailing week even when `limit` caps the row list.","security":[{"ApiKey":["whiteboard:read"]}],"parameters":[{"schema":{"$ref":"#/components/schemas/SimilarityDecision"},"required":false,"name":"decision","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":500,"default":100},"required":false,"name":"limit","in":"query"}],"responses":{"200":{"description":"Decision rows + 7-day summary.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimilarityLogResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the whiteboard:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/factcards":{"get":{"tags":["Fact-cards"],"summary":"List fact-check claims","description":"Returns the most recent fact-check claims, newest first. Filter by `parent_kind` + `parent_id` to scope to one whiteboard card, or by `verdict`/`claim_category` to slice by quality. `next_cursor` opaque token for pagination — pass back as `cursor`.","security":[{"ApiKey":["factcards:read"]}],"parameters":[{"schema":{"$ref":"#/components/schemas/FactCardParentKind"},"required":false,"name":"parent_kind","in":"query"},{"schema":{"type":"string"},"required":false,"name":"parent_id","in":"query"},{"schema":{"$ref":"#/components/schemas/FactCardStatus"},"required":false,"name":"status","in":"query"},{"schema":{"$ref":"#/components/schemas/ClaimCategory"},"required":false,"name":"claim_category","in":"query"},{"schema":{"$ref":"#/components/schemas/Verdict"},"required":false,"name":"verdict","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":200,"default":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"List of fact-card rows.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListFactCardsResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the factcards:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/factcards/{factCardId}":{"get":{"tags":["Fact-cards"],"summary":"Get a single fact-check claim","security":[{"ApiKey":["factcards:read"]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"factCardId","in":"path"}],"responses":{"200":{"description":"Fact-card row.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FactCard"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the factcards:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Unknown fact_card_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/factcards/{factCardId}/similar":{"get":{"tags":["Fact-cards"],"summary":"Find semantically similar prior fact-cards","description":"Embeds the target card's English claim text with `@cf/baai/bge-m3` and returns the top-K cosine-similarity neighbours from the fact-card Vectorize index. Pass `same_category=1` to require a category match (so a numerical claim only matches numerical priors).","security":[{"ApiKey":["factcards:read"]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"factCardId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":20,"default":5},"required":false,"name":"top_k","in":"query"},{"schema":{"type":"string","enum":["0","1"]},"required":false,"name":"same_category","in":"query"}],"responses":{"200":{"description":"Top-K similar prior fact-cards plus the reuse-policy table.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimilarFactsResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the factcards:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/data/{date}/topics":{"get":{"tags":["Reports"],"summary":"List shared-data topics ingested for a given work date","description":"Each work-date morning the data-scientist runs an ingest that snapshots a curated set of market data into shared topics. This endpoint enumerates which topics landed for the given `YYYY-MM-DD` (Asia/Singapore work-date).","security":[{"ApiKey":["reports:read"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"date","in":"path"}],"responses":{"200":{"description":"Available topics for the date.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedDataTopicsResponse"}}}},"400":{"description":"Invalid date format.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/api/v1/data/{date}/{topic}":{"get":{"tags":["Reports"],"summary":"Read a single shared-data topic JSON","description":"Returns the ingested JSON document for `(date, topic)`. 404 if the date's ingest didn't run or didn't include this topic. Topic names accept an optional `.json` suffix for curl ergonomics.","security":[{"ApiKey":["reports:read"]}],"parameters":[{"schema":{"type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$"},"required":true,"name":"date","in":"path"},{"schema":{"type":"string"},"required":true,"name":"topic","in":"path"}],"responses":{"200":{"description":"Topic document.","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"nullable":true}}}}},"400":{"description":"Invalid date format.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"401":{"description":"Missing or invalid X-API-Key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"403":{"description":"Key lacks the reports:read scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"404":{"description":"Document or topic not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}