{"openapi":"3.1.0","info":{"title":"Investeam Public API","description":"The public investment-pipeline API: create a brief, answer clarifications, convene a committee, and poll execution, transcript, and analysis. Every mutation is submit→poll; every non-2xx returns the standard error envelope `{error:{code,message,request_id,retryable}}`. Authenticate with `Authorization: Bearer <token>` — either a Firebase ID token (browser) or a programmatic `hfk_` key (agent).\n\n**Disclosure — read before integrating.** Every response that carries AI-generated output includes a `disclosure` object. The output is **AI-generated and may contain errors, and is not investment advice** — not a recommendation, solicitation, or offer, not tailored to anyone's circumstances, and it creates no advisory or fiduciary relationship. By using this API you agree to the [Developer / API Terms of Use](https://investeam.io/terms): in particular, you must surface this not-advice + AI-generated disclosure to your end users (in their language; Hebrew where your users read Hebrew) and you must **not** remove or alter the `disclosure` field. You may not present this output as investment advice, investment marketing, or portfolio management without holding the licences that activity requires.","version":"0.1.0"},"paths":{"/health":{"get":{"tags":["meta"],"summary":"Health","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Health Health Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/auth/config":{"get":{"tags":["auth"],"summary":"Auth Config","description":"Public Identity Platform bootstrap values for the browser UI.","operationId":"auth_config_api_v1_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthConfig"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/auth/me":{"get":{"tags":["auth"],"summary":"Me","description":"Provision (idempotently) and return the signed-in user's profile.","operationId":"me_api_v1_auth_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":{"type":"object","additionalProperties":true,"title":"Response Me Api V1 Auth Me Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["auth"],"summary":"Delete Me","description":"Self-service GDPR Art. 17 erasure of the signed-in user.\n\nCascades deletion of every user-owned document across the platform\n(brief_sessions → orchestration_sessions → committee_executions +\nsubcollections → synthesis_analyses → the BFF's own user_reviews), then\ndeletes the Firebase Auth identity + users/{uid} doc LAST.\n\n**Why identity-last** (see ``ErasureGateway``): the downstream hops carry the\nBFF's own service API key and service OIDC token, not the caller's ID token,\nso deleting the identity early would not 401 them. It would, however, remove\nthe only way the *subject* can reach this endpoint again — stranding whatever\nis left behind a dead account. The identity therefore goes only once every\nother store is confirmed erased.\n\nTwo outcomes: **204** when everything was erased in this request (the next\nauthenticated call 401s — the token is now stale), or **202** when a store\ncould not be swept. A 202 means the request is recorded and will be\ncompleted; the account deliberately still exists so the user can retry.\n\nTwo fail-closed flags — a permanent endpoint switch and the ``ff_2026_07_30``\nsaga day-flag. ``erasure_endpoint`` gates the endpoint itself: off ⇒ 404, and\nthe fallback in privacy.html (``contact@investeam.io``) is the erasure path.\n``ff_2026_07_30`` gates the durable saga: off ⇒ the pre-saga cascade\n(fail-fast, a 5xx on a hop failure, no record written).","operationId":"delete_me_api_v1_auth_me_delete","parameters":[{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"202":{"description":"The erasure was durably accepted but not finished in this request: a store could not be swept, so the account is kept alive and the erasure completes on retry or by the reconcile sweep."},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/briefs":{"post":{"tags":["briefs"],"summary":"Create Brief","description":"Submit input. Returns a rejection, clarifying questions, or a brief.\n\nA spend-triggering submit: an ``hfk_`` principal needs ``pipeline:write`` and\nis subject to the per-key rate limit + daily kickoff quota (H-3). A supplied\n``Idempotency-Key`` dedups a retried/concurrent submit to one kickoff (A-1;\nno header ⇒ unchanged). Not applied to ``/answers`` (not a pipeline start).","operationId":"create_brief_api_v1_briefs_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBriefRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Brief Api V1 Briefs Post"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/sessions":{"get":{"tags":["briefs"],"summary":"List Sessions","description":"List the signed-in user's sessions (newest-first), owned upstream.\n\nExcluded from the per-session poll floor: a list read has no session_id.","operationId":"list_sessions_api_v1_sessions_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 Sessions Api V1 Sessions Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/briefs/{session_id}/answers":{"post":{"tags":["briefs"],"summary":"Submit Answers","description":"Answer the clarifying questions and generate the brief.","operationId":"submit_answers_api_v1_briefs__session_id__answers_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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/SubmitAnswersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Submit Answers Api V1 Briefs  Session Id  Answers Post"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/briefs/{session_id}":{"get":{"tags":["briefs"],"summary":"Get Brief","description":"Fetch a session and its brief (per-session poll floor for hfk_ keys).","operationId":"get_brief_api_v1_briefs__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Brief Api V1 Briefs  Session Id  Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/executions/{session_id}":{"get":{"tags":["executions"],"summary":"Get Execution","description":"Fetch the committee execution's status and any findings so far.\n\nHot poll path: an ``hfk_`` key is held to the per-session floor (HF-159 #2).","operationId":"get_execution_api_v1_executions__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Execution Api V1 Executions  Session Id  Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/executions/{session_id}/transcript":{"get":{"tags":["executions"],"summary":"Get Transcript","description":"Fetch transcript entries after seq ``after``. A 404 means \"not started\nyet\" — same polling contract as the execution status read.","operationId":"get_transcript_api_v1_executions__session_id__transcript_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"after","in":"query","required":false,"schema":{"type":"integer","default":0,"title":"After"}},{"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 Transcript Api V1 Executions  Session Id  Transcript Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/orchestrations":{"post":{"tags":["orchestrations"],"summary":"Create Orchestration","description":"Run the forum over a completed brief; returns the committee + task plan.\n\nThe end user's **effective** tier is forwarded (as ``X-User-Tier``, via the\ngateway) so the forum and the committee it dispatches run on the tier the\nuser actually pays for — without it the orchestrator defaults to pro. HF-199:\na free user's first committee execution is upgraded to pro once (the entitlement\nis consumed inside the kickoff closure, so a replay never burns a second one).\nThe verified user id rides along as ``X-User-Id`` so the orchestrator can\nenforce ownership. A supplied ``Idempotency-Key`` dedups a retried/concurrent\nconvene to one committee kickoff (A-1; no header ⇒ unchanged).","operationId":"create_orchestration_api_v1_orchestrations_post","parameters":[{"name":"Idempotency-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency-Key"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrchestrationRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Create Orchestration Api V1 Orchestrations Post"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/orchestrations/{session_id}":{"get":{"tags":["orchestrations"],"summary":"Get Orchestration","description":"Fetch a persisted orchestration plan (per-session poll floor for hfk_).","operationId":"get_orchestration_api_v1_orchestrations__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Orchestration Api V1 Orchestrations  Session Id  Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/orchestrations/{session_id}/convene":{"post":{"tags":["orchestrations"],"summary":"Convene Orchestration","description":"Dispatch the committee for a ready plan (design-guidelines §8.3 convene gate).\n\nA mutation (``pipeline:write``) with no body: the orchestrator reads the\npersisted plan. A double-convene is an idempotent no-op upstream — the\n``plan_published`` guard collapses a retry to one committee run — so no\nedge-side dedup is needed here. The verified user id rides as ``X-User-Id`` so\nthe orchestrator enforces ownership. The upstream envelope is returned\nunchanged: **202** on dispatch, **409** ``conflict`` when the plan is still\ngenerating or failed (the message is shown verbatim), **404** when the session\nis missing/not-owned — or, while the split day-flag is off, because the convene\nsurface does not exist yet (fail-closed, keep the legacy auto-dispatch flow).","operationId":"convene_orchestration_api_v1_orchestrations__session_id__convene_post","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Convene Orchestration Api V1 Orchestrations  Session Id  Convene Post"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/analyses/{session_id}":{"get":{"tags":["analyses"],"summary":"Get Analysis","description":"Fetch the synthesis analysis's status and its result once produced.\n\nGated behind ``pipeline:findings``: the synthesized analysis is the premium\nobject, so a read-only (``pipeline:read``) key can poll execution *status*\nbut not read the findings (QA gate — the ``findings`` scope was otherwise\ndead code).","operationId":"get_analysis_api_v1_analyses__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Analysis Api V1 Analyses  Session Id  Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/summaries/{session_id}":{"get":{"tags":["summaries"],"summary":"Get Summary","description":"Fetch the plain-language debrief's status and its result once produced.\n\nGated behind ``pipeline:findings``: the debrief is the premium object, so a\nread-only (``pipeline:read``) key can poll execution *status* elsewhere but\nnot read the summary. A 404 means \"no debrief yet\" — the browser polls.","operationId":"get_summary_api_v1_summaries__session_id__get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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 Summary Api V1 Summaries  Session Id  Get"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/summaries/{session_id}/audit.pdf":{"get":{"tags":["summaries"],"summary":"Get Audit Pdf","description":"Download the debrief's audit record as a PDF (HF-227).\n\nGated behind ``pipeline:findings`` — the audit record projects the same\npremium findings object ``GET /summaries/{id}`` serves — and behind the day\nflag, both fail-closed. A 404 covers every \"you cannot have this\" case\n(flag off, malformed id, no debrief, no audit record, foreign session), so\nthe surface leaks nothing about which one applies; a non-English session is\nthe one refusal with its own code (``409 unsupported_language``), forwarded\nfrom the debrief service because the UI shows that reason verbatim.","operationId":"get_audit_pdf_api_v1_summaries__session_id__audit_pdf_get","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"The audit record as a PDF document.","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/summaries/{session_id}/feedback":{"put":{"tags":["summaries"],"summary":"Put Feedback","description":"Record the signed-in user's like/dislike (and optional comment).\n\nHuman-only: a programmatic ``hfk_`` principal is 403ed upstream of this by\n``require_human_user``. The verified uid is forwarded as ``X-User-Id`` so the\ndebrief service scopes the feedback to its owner (404 on a foreign session).","operationId":"put_feedback_api_v1_summaries__session_id__feedback_put","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session 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/FeedbackRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Put Feedback Api V1 Summaries  Session Id  Feedback Put"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]},"delete":{"tags":["summaries"],"summary":"Delete Feedback","description":"Clear the signed-in user's feedback (idempotent — always 204).\n\nSame human-only gating as the PUT: a programmatic ``hfk_`` principal is\n403ed by ``require_human_user``. A 404 (unknown/foreign session) passes\nthrough from the debrief service.","operationId":"delete_feedback_api_v1_summaries__session_id__feedback_delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"Authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/reviews":{"post":{"tags":["reviews"],"summary":"Post Review","description":"Upsert the signed-in user's review of one AI response.\n\nHuman-only (a programmatic ``hfk_`` principal is 403ed by ``require_human_user``).\nGated behind the shipping-day flag — off ⇒ 404 (mirrors the erasure gateway\nfail-closed pattern). Never logs ``comment`` (it is user-generated content).","operationId":"post_review_api_v1_reviews_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/ReviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReviewResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}},"/api/v1/reviews/{response_type}/{session_id}":{"get":{"tags":["reviews"],"summary":"Get Review","description":"Fetch the signed-in user's review for one AI response, or 404 if absent.\n\nSame auth + flag gate as the POST. The uid comes from the verified token —\na caller can only read their own review.","operationId":"get_review_api_v1_reviews__response_type___session_id__get","parameters":[{"name":"response_type","in":"path","required":true,"schema":{"enum":["brief","committee","debrief"],"type":"string","title":"Response Type"}},{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}},{"name":"task_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task 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/ReviewResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"},"502":{"$ref":"#/components/responses/UpstreamError"}},"security":[{"bearerAuth":[]}]}}},"components":{"schemas":{"AnswerInput":{"properties":{"question_id":{"type":"string","title":"Question Id"},"selected_options":{"items":{"type":"string"},"type":"array","title":"Selected Options"},"custom_answer":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Custom Answer"}},"type":"object","required":["question_id"],"title":"AnswerInput"},"AuthConfig":{"properties":{"apiKey":{"type":"string","title":"Apikey"},"authDomain":{"type":"string","title":"Authdomain"}},"type":"object","required":["apiKey","authDomain"],"title":"AuthConfig","description":"Public Identity Platform bootstrap values for the browser UI.\n\nSerialized in the camelCase shape ``firebase.initializeApp`` expects.\nThe Web API key is an identifier, not a secret."},"CreateBriefRequest":{"properties":{"input":{"type":"string","maxLength":20000,"minLength":1,"title":"Input","description":"A question, stock, or topic."}},"type":"object","required":["input"],"title":"CreateBriefRequest"},"CreateOrchestrationRequest":{"properties":{"session_id":{"type":"string","minLength":1,"title":"Session Id"},"brief":{"additionalProperties":true,"type":"object","title":"Brief","description":"The Master Investment Brief JSON."}},"type":"object","required":["session_id","brief"],"title":"CreateOrchestrationRequest","description":"Kick off the forum for a completed brief.\n\nThe brief is passed straight through to the orchestrator (which validates\nit), so the BFF models it loosely to stay resilient to brief-schema growth."},"FeedbackRequest":{"properties":{"rating":{"type":"string","enum":["like","dislike"],"title":"Rating"},"comment":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Comment"}},"type":"object","required":["rating"],"title":"FeedbackRequest","description":"A human's like/dislike on the debrief, validated at the public boundary."},"ReviewRequest":{"properties":{"response_type":{"type":"string","enum":["brief","committee","debrief"],"title":"Response Type"},"session_id":{"type":"string","maxLength":200,"minLength":1,"title":"Session Id"},"task_id":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Task Id"},"rating":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Rating"},"comment":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Comment"}},"additionalProperties":false,"type":"object","required":["response_type","session_id","rating"],"title":"ReviewRequest","description":"The edge model for POST /reviews — validated before touching the repo."},"ReviewResponse":{"properties":{"uid":{"type":"string","title":"Uid"},"response_type":{"type":"string","enum":["brief","committee","debrief"],"title":"Response Type"},"session_id":{"type":"string","title":"Session Id"},"task_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task Id"},"rating":{"type":"integer","title":"Rating"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"updated_at":{"type":"string","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["uid","response_type","session_id","task_id","rating","comment","updated_at"],"title":"ReviewResponse","description":"The public shape of a persisted review — same for POST reply and GET."},"SubmitAnswersRequest":{"properties":{"answers":{"items":{"$ref":"#/components/schemas/AnswerInput"},"type":"array","minItems":1,"title":"Answers"}},"type":"object","required":["answers"],"title":"SubmitAnswersRequest"},"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"object","required":["code","message","request_id","retryable"],"properties":{"code":{"type":"string","description":"Closed set of machine-readable error codes.","enum":["bad_request","unauthorized","forbidden","not_found","conflict","rate_limited","upstream_error","internal_error"]},"message":{"type":"string","description":"Human-readable message; shown to the user verbatim."},"request_id":{"type":"string","description":"Edge-generated correlation id (also the `X-Request-Id` header)."},"retryable":{"type":"boolean","description":"Whether a well-behaved client may retry with back-off."}}}}},"Disclosure":{"type":"object","description":"Legal disclosure the Licensee must surface to end users and must not strip or alter (API Terms §3). Bilingual EN + HE always.","required":["notice","not_advice","ai_generated","terms_url","must_display_to_end_user","marking"],"properties":{"notice":{"type":"object","required":["en","he"],"properties":{"en":{"type":"string","description":"English not-advice + AI notice."},"he":{"type":"string","description":"Hebrew not-advice + AI notice."}}},"not_advice":{"type":"boolean","enum":[true]},"ai_generated":{"type":"boolean","enum":[true]},"terms_url":{"type":"string","format":"uri","example":"https://investeam.io/terms"},"must_display_to_end_user":{"type":"boolean","enum":[true]},"marking":{"type":"object","description":"EU AI Act Art. 50(2) machine-readable marking of the generated Output. Also carried as the X-AI-Generated / X-AI-Marking response headers, which reach non-JSON bodies too.","required":["artificially_generated","spec","generator"],"properties":{"artificially_generated":{"type":"boolean","enum":[true]},"spec":{"type":"string","example":"investeam-ai-marking/1"},"generator":{"type":"string"}}}}}},"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"A Firebase ID token or a programmatic `hfk_` API key."}},"responses":{"BadRequest":{"description":"The request could not be understood.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Unauthorized":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Forbidden":{"description":"Authenticated but not permitted (missing scope or claim).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"NotFound":{"description":"The resource does not exist (yet).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Optimistic-concurrency clash.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"A per-principal rate or quota limit was hit.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UpstreamError":{"description":"An upstream service is temporarily unavailable.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"servers":[{"url":"/","description":"This BFF origin"}],"security":[{"bearerAuth":[]}]}