{"openapi":"3.1.0","info":{"title":"so-yesterday.ai","description":"Curated AI transformation knowledge base. REST API for agents and developers. See /llms.txt for an agent-friendly overview, /SKILL.md for the prose reference, /.well-known/agent.json for the machine-readable capability doc.","contact":{"name":"so-yesterday.ai","url":"https://so-yesterday.ai/about"},"version":"3.0"},"servers":[{"url":"https://so-yesterday.ai","description":"Production"}],"paths":{"/api/oauth/v2/register":{"post":{"tags":["mcp-oauth","mcp-oauth"],"summary":"Dynamic Client Registration (RFC 7591)","description":"Register a new OAuth client (no auth required — trust is at consent).\n\nPublic clients (``token_endpoint_auth_method='none'``) use PKCE; no\nclient_secret is returned.  Confidential clients (``client_secret_basic``)\nget a generated client_secret (shown once; stored hashed).\n\nNo deduplication: each registration call creates a distinct client_id.\n\n**AUDIT-006: elevated-scope gating.** When the request asks for\n`moderate` or `admin` (the elevated scopes — see\n:data:`app.services.oauth.scope_policy._ELEVATED_SCOPES`), those scopes\nare only granted when the request's ``client_uri`` is an exact-string\nmatch against ``settings.dcr_elevated_scope_allowlist``. Otherwise the\nelevated scopes are stripped silently from the registration and the\nresponse's ``scope`` field reflects the actual granted set (RFC 7591\n§3.2.1 — server may override what the client requested). A warning\nis logged on every downgrade so operators can spot abuse.","operationId":"register_client_api_oauth_v2_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_DCRRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/v2/authorize":{"get":{"tags":["mcp-oauth","mcp-oauth"],"summary":"PKCE Authorization Endpoint","description":"PKCE authorization endpoint (browser-facing, session required).\n\nIf the user is not logged in, redirects to the Google login page with\n``next=`` set to the original authorize URL.  On approval, issues a\nsingle-use auth code and redirects back.","operationId":"authorize_api_oauth_v2_authorize_get","parameters":[{"name":"response_type","in":"query","required":false,"schema":{"type":"string","default":"","title":"Response Type"}},{"name":"client_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Client Id"}},{"name":"redirect_uri","in":"query","required":false,"schema":{"type":"string","default":"","title":"Redirect Uri"}},{"name":"code_challenge","in":"query","required":false,"schema":{"type":"string","default":"","title":"Code Challenge"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"type":"string","default":"S256","title":"Code Challenge Method"}},{"name":"scope","in":"query","required":false,"schema":{"type":"string","default":"","title":"Scope"}},{"name":"state","in":"query","required":false,"schema":{"type":"string","default":"","title":"State"}},{"name":"resource","in":"query","required":false,"schema":{"type":"string","default":"","title":"Resource"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/v2/token":{"post":{"tags":["mcp-oauth","mcp-oauth"],"summary":"Token Exchange (RFC 6749)","description":"Token endpoint supporting authorization_code and refresh_token grants.\n\nAccepts application/x-www-form-urlencoded (as required by RFC 6749).\nPKCE code_verifier is validated with constant-time compare against the\nstored S256 challenge.  Refresh tokens are rotated on every use;\npresenting a rotated (used) token triggers grant-family revocation.","operationId":"token_api_oauth_v2_token_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/oauth/v2/revoke":{"post":{"tags":["mcp-oauth","mcp-oauth"],"summary":"Token Revocation (RFC 7009)","description":"Revoke an access or refresh token.\n\nPer RFC 7009, the endpoint always returns 200 (revocation is a hint).\nIf the token is unknown or already revoked, the response is still 200.","operationId":"revoke_api_oauth_v2_revoke_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/google/login":{"get":{"tags":["auth"],"summary":"Google Login","operationId":"google_login_api_auth_google_login_get","parameters":[{"name":"next","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next"}},{"name":"next_user_code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next User Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/google/callback":{"get":{"tags":["auth"],"summary":"Google Callback","operationId":"google_callback_api_auth_google_callback_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/me":{"get":{"tags":["auth","agent"],"summary":"Whoami","description":"Token / session verification for agents.\n\nAnonymous-safe: returns ``{authenticated: false}`` when no credentials\nare presented, ``{authenticated: true, ...}`` otherwise. Intentionally\nomits email and PII — agents only need to know whether the key works\nand what they can do with it. For the full profile, sessions use\n``GET /api/account/me`` (cookie-only).","operationId":"whoami_api_auth_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/auth/logout":{"post":{"tags":["auth"],"summary":"Logout","operationId":"logout_api_auth_logout_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/account/me":{"get":{"tags":["account"],"summary":"Get Me","operationId":"get_me_api_account_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeOut"}}}}}},"patch":{"tags":["account"],"summary":"Update Me","operationId":"update_me_api_account_me_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/check-handle":{"post":{"tags":["account"],"summary":"Check Handle","description":"Unauthenticated availability probe for the setup page.\n\nValidates ``body.handle`` against ``HANDLE_REGEX``; returns\n``available=False`` with a list of suggestions when the handle is\ntaken or invalid. Rate-limited 60/min per IP to keep enumeration\ncheap-but-bounded.\n\nAuth: NONE (so the unauthenticated setup page can probe). The\nonly data leaked is \"is this handle taken yes/no\", which is\npublicly visible anyway at ``/u/<handle>``.","operationId":"check_handle_api_account_check_handle_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandleCheckRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandleCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/setup":{"post":{"tags":["account"],"summary":"Setup Account","description":"First-login choose-handle + nickname.\n\nRefuses once the user already has a handle — use\n``PATCH /api/account/handle`` instead. On collision returns 422\nwith ``{detail: \"handle taken\", suggestions: [...]}`` so the form\ncan offer a one-click fix.","operationId":"setup_account_api_account_setup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSetupRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/nickname":{"patch":{"tags":["account"],"summary":"Update Nickname","description":"Change the user's nickname. No rate limit (cheap edit).","operationId":"update_nickname_api_account_nickname_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NicknameUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/handle":{"patch":{"tags":["account"],"summary":"Update Handle","description":"Change the user's handle. Rate-limited 1 change per 30 days.","operationId":"update_handle_api_account_handle_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandleUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/api-keys":{"get":{"tags":["account"],"summary":"List Api Keys","operationId":"list_api_keys_api_account_api_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/ApiKeyOut"},"type":"array","title":"Response List Api Keys Api Account Api Keys Get"}}}}}},"post":{"tags":["account"],"summary":"Create Api Key","operationId":"create_api_key_api_account_api_keys_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApiKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/api-keys/{key_id}":{"delete":{"tags":["account"],"summary":"Revoke Api Key","operationId":"revoke_api_key_api_account_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/account/oauth-clients":{"get":{"tags":["account"],"summary":"List Oauth Clients","description":"List all OAuth clients for which this user has active grants.","operationId":"list_oauth_clients_api_account_oauth_clients_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OAuthClientOut"},"type":"array","title":"Response List Oauth Clients Api Account Oauth Clients Get"}}}}}}},"/api/account/oauth-clients/{client_id}":{"delete":{"tags":["account"],"summary":"Revoke Oauth Client","description":"Revoke all of this user's active grants for an OAuth client.","operationId":"revoke_oauth_client_api_account_oauth_clients__client_id__delete","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/proposals":{"get":{"tags":["proposals"],"summary":"Public Proposals List","description":"List canon proposals filtered by status bucket.\n\n- `pending` → status='pending' (sorted by confidence high→low, then recency)\n- `decided` → status IN ('accepted','rejected','applied','superseded')\n- `all` → no filter\n\nOptional ``kind`` query param restricts results to one proposal kind\n(e.g. ``link_add``, ``tag_correction``, ``concept_stub``,\n``essay_promotion``). When omitted, all kinds are returned.\n\nPagination via `limit` (max 100) and `offset`.","operationId":"public_proposals_list_api_proposals_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"type":"string","pattern":"^(pending|decided|all)$","default":"pending","title":"Status"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicProposalList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/proposals/{proposal_id}/flag":{"post":{"tags":["proposals"],"summary":"Flag Proposal","description":"Flag a proposal as bad/spam/incorrect.\n\nIdempotent per-user: a second flag from the same user returns 409. Flags\naccumulate in ``evidence.community_flags``; once the count crosses\n``settings.AUTO_REJECT_FLAG_THRESHOLD`` the proposal is auto-rejected\n(status='rejected', decided_by=null, no apply).","operationId":"flag_proposal_api_proposals__proposal_id__flag_post","parameters":[{"name":"proposal_id","in":"path","required":true,"schema":{"type":"string","title":"Proposal Id"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalFlagIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProposalFlagOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments":{"get":{"tags":["comments"],"summary":"List Comments","operationId":"list_comments_api_comments_get","parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","title":"Entity Type"}},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","title":"Entity Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"include_hidden","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Hidden"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["comments"],"summary":"Create Comment","operationId":"create_comment_api_comments_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/create":{"get":{"tags":["comments","comments","agent"],"summary":"Create Comment Via Get","description":"Create a comment via GET for sandboxed agents.\n\nRequires a sok_ token issued via ``GET /api/oauth/token``. Bearer in\n``Authorization`` header. Body is URL-encoded; max 1500 chars.\nMandatory ``idempotency_key`` for retry safety. Response carries\n``Cache-Control: no-store``. Rate-limited 3/min separate from POST.","operationId":"create_comment_via_get_api_comments_create_get","parameters":[{"name":"entity_type","in":"query","required":true,"schema":{"type":"string","description":"post / video / essay / digest / knowledge","title":"Entity Type"},"description":"post / video / essay / digest / knowledge"},{"name":"entity_id","in":"query","required":true,"schema":{"type":"string","maxLength":200,"title":"Entity Id"}},{"name":"body","in":"query","required":true,"schema":{"type":"string","maxLength":1500,"title":"Body"}},{"name":"idempotency_key","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":128,"title":"Idempotency Key"}},{"name":"parent_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"}},{"name":"kind","in":"query","required":false,"schema":{"type":"string","description":"content | schema | artifact | watchlist","default":"content","title":"Kind"},"description":"content | schema | artifact | watchlist"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/{comment_id}":{"get":{"tags":["comments"],"summary":"Get Comment","operationId":"get_comment_api_comments__comment_id__get","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["comments"],"summary":"Edit Comment","operationId":"edit_comment_api_comments__comment_id__patch","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["comments"],"summary":"Delete Comment","operationId":"delete_comment_api_comments__comment_id__delete","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/{comment_id}/react":{"post":{"tags":["comments"],"summary":"Add Reaction","operationId":"add_reaction_api_comments__comment_id__react_post","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/{comment_id}/react/{kind}":{"delete":{"tags":["comments"],"summary":"Remove Reaction","operationId":"remove_reaction_api_comments__comment_id__react__kind__delete","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string","title":"Kind"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReactionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["comments","comments","agent"],"summary":"Add Reaction Via Get","description":"Add a reaction via GET for sandboxed agents.\n\nRequires a sok_ token issued via ``GET /api/oauth/token``.\nReactions are naturally idempotent (duplicate reactions are no-ops),\nso ``idempotency_key`` is here for parity + caller-side retry safety.","operationId":"add_reaction_via_get_api_comments__comment_id__react__kind__get","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}},{"name":"kind","in":"path","required":true,"schema":{"type":"string","title":"Kind"}},{"name":"idempotency_key","in":"query","required":true,"schema":{"type":"string","minLength":8,"maxLength":128,"title":"Idempotency Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/comments/{comment_id}/mark":{"post":{"tags":["comments"],"summary":"Mark Comment","description":"Apply an approval / soft-delete mark to a comment.\n\nAuth: global moderator only.\n\nBody:\n  - ``mark`` — \"approved\" | \"disapproved\" | \"marked_for_deletion\"\n  - ``evidence`` — optional one-sentence rationale\n\nWhen ``mark == \"marked_for_deletion\"``, also sets\n``comment.marked_for_deletion_at = now()`` so the soft-delete\nprojection takes effect immediately for non-mod readers.\n\nReturns the updated CommentMark row as a dict.","operationId":"mark_comment_api_comments__comment_id__mark_post","parameters":[{"name":"comment_id","in":"path","required":true,"schema":{"type":"string","title":"Comment Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommentMarkBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/device/lookup":{"get":{"tags":["device"],"summary":"Lookup Device","description":"Fetch row metadata for the consent page. Public read (the user_code\nIS the unguessable token). Returns 404 if not found, 410 if expired or\nno longer pending.\n\nWhen a signed-in viewer makes the call we also include their role in the\nresponse so the consent UI can surface an elevated-capabilities warning\nwhen the requested scopes intersect with moderator/admin powers.","operationId":"lookup_device_api_device_lookup_get","parameters":[{"name":"user_code","in":"query","required":true,"schema":{"type":"string","maxLength":24,"title":"User Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceLookupOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/device/approve":{"post":{"tags":["device"],"summary":"Approve Device","description":"Record the signed-in user's approve/deny decision.","operationId":"approve_device_api_device_approve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceApproveRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/device":{"post":{"tags":["oauth"],"summary":"Start Device Authorization","description":"RFC 8628 §3.1 — initiate a new device-authorization request.","operationId":"start_device_authorization_api_oauth_device_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["oauth"],"summary":"Start Device Authorization Get","description":"GET alias for sandboxes that can't POST. Same response shape as the POST.\n\nNon-RFC, pragmatic: some agent runtimes (notably claude.ai web fetch /\nChatGPT browsing) block outbound POSTs to user-named URLs but still\ndo GETs. The GET-only polling path now also exists at GET /token —\nsee ``exchange_device_token_get`` — with a 1h TTL and ``issued_via_get``\nflag for the GET-write endpoints. Together they let a fully POST-\nblocked agent complete the flow.\n\nResponse carries ``Cache-Control: no-store`` so intermediate proxies\ndon't replay one user_code across multiple agent retries.","operationId":"start_device_authorization_get_api_oauth_device_get","parameters":[{"name":"client_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Name"}},{"name":"client_host","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Host"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/oauth/token":{"post":{"tags":["oauth"],"summary":"Exchange Device Token","description":"RFC 8628 §3.4 — agent polls here. Errors are JSON 400 with ``error``.","operationId":"exchange_device_token_api_oauth_token_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Exchange Device Token Api Oauth Token Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["oauth"],"summary":"Exchange Device Token Get","description":"GET alias for ``POST /api/oauth/token`` — sandboxed-agent path.\n\nUsed by agents whose HTTP tool only supports GET (claude.ai web\nfetch, ChatGPT browsing). Identical state machine to the POST\nvariant, but the issued key:\n\n  * has a 1h TTL (vs 90d for POST-issued keys)\n  * is flagged ``issued_via_get=True`` so the GET-write endpoints\n    (``GET /api/comments``, ``GET /api/comments/{id}/react``)\n    recognise it\n\nThe response carries ``Cache-Control: no-store`` to keep the token\nout of intermediate caches.","operationId":"exchange_device_token_get_api_oauth_token_get","parameters":[{"name":"grant_type","in":"query","required":false,"schema":{"type":"string","default":"device_code","title":"Grant Type"}},{"name":"device_code","in":"query","required":false,"schema":{"type":"string","default":"","title":"Device Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Exchange Device Token Get Api Oauth Token Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts":{"get":{"tags":["posts"],"summary":"List Posts","operationId":"list_posts_api_posts_get","parameters":[{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Tag"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","pattern":"^(newest|oldest)$","default":"newest","title":"Sort"}},{"name":"has_candidate","in":"query","required":false,"schema":{"type":"boolean","description":"Filter to posts that have a pending summary_candidate_md. Populates summary_candidate_byot_model + summary_candidate_generated_at + summary_byot_model on each item so the admin candidate-queue UI can render the diff context without N+1 fetches.","default":false,"title":"Has Candidate"},"description":"Filter to posts that have a pending summary_candidate_md. Populates summary_candidate_byot_model + summary_candidate_generated_at + summary_byot_model on each item so the admin candidate-queue UI can render the diff context without N+1 fetches."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["posts"],"summary":"Create Post","operationId":"create_post_api_posts_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/me":{"get":{"tags":["posts"],"summary":"List My Posts","operationId":"list_my_posts_api_posts_me_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{id_or_slug}":{"get":{"tags":["posts"],"summary":"Get Post","description":"Look up a single post by either UUID or slug.\n\nAccepts both forms because frontends commonly hold the UUID right after\n``POST /api/posts`` while permalinks use the slug. Resolver tries UUID\nfirst, falls back to slug. Returns 404 with a clear \"Post not found\"\nmessage when neither matches.","operationId":"get_post_api_posts__id_or_slug__get","parameters":[{"name":"id_or_slug","in":"path","required":true,"schema":{"type":"string","title":"Id Or Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}":{"patch":{"tags":["posts"],"summary":"Edit Post","operationId":"edit_post_api_posts__post_id__patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["posts"],"summary":"Delete Post","operationId":"delete_post_api_posts__post_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/vision":{"patch":{"tags":["posts"],"summary":"Patch Post Vision","description":"Update the topic-page ``vision`` for a post.\n\nAuthorised to: originator OR any moderator OR any post-level moderator\n(``post_moderator`` row — Epic B). The originator is treated as the\nadmin of their topic (per the topic-page design), so they have the\nsame vision-edit privileges as a mod. Forbidden in terminal /\ndead-end states (``rejected``, ``retracted``, ``archived``) because\nthere's no live topic-page to steer at that point.\n\nEmits a single ``post.vision.changed`` audit event with the before/after\nin the payload. The audit chain stays valid afterward (covered by\n``test_vision_patch_keeps_audit_chain_valid``).","operationId":"patch_post_vision_api_posts__post_id__vision_patch","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostVisionUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary-context":{"get":{"tags":["posts"],"summary":"Get Summary Context","description":"Return the structured prompt context an agent needs to BYOT-generate a summary.\n\nAuth: originator OR global moderator OR post-level moderator. Same gate as\nPOST /generate-summary.  API-key callers must have MODERATE scope (session\ncallers need role>=moderator OR originator status).\n\nThe payload includes title, vision, body_md, prior summary_md, all comments\ngrouped by kind with useful_count + short_id, schema_axes, mode_hint, budget,\nand catalogue_hash.  ETag is sha256 of the full payload (quoted weak ETag per\nRFC 7232).  The same value is echoed in ``context_etag`` for JSON-only callers.\n\n``?include_hidden=true`` exposes hidden/deleted comments — restricted to\nglobal moderators (originators and post-mods do not get hidden content).","operationId":"get_summary_context_api_posts__post_id__summary_context_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"preserve_sections","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated list of section-heading substrings or comment-id prefixes (c-XXXXXX) that the agent should preserve. Max 20 tokens, each max 80 chars. Propagated verbatim into the payload.","title":"Preserve Sections"},"description":"Comma-separated list of section-heading substrings or comment-id prefixes (c-XXXXXX) that the agent should preserve. Max 20 tokens, each max 80 chars. Propagated verbatim into the payload."},{"name":"include_hidden","in":"query","required":false,"schema":{"type":"boolean","description":"When true, include soft-deleted/hidden comments in the context. Moderator-only option — the hidden state is internal.","default":false,"title":"Include Hidden"},"description":"When true, include soft-deleted/hidden comments in the context. Moderator-only option — the hidden state is internal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/generate-summary":{"post":{"tags":["posts"],"summary":"Generate Summary","description":"Regenerate the topic-page summary for a post.\n\nAuth: originator OR global moderator OR post-level moderator\n(``post_moderator`` row — Epic B). Same pattern as\n``PATCH /api/posts/{id}/vision``. Refuses on terminal states\n(rejected / retracted / archived) — those posts have no live\ntopic-page to summarise.\n\nConcurrency-capped at ``APP_POST_SUMMARY_CONCURRENCY`` (default 2)\nvia a process-wide semaphore in ``services.topic_summary``. Excess\ncallers block until a slot opens — prevents a Starlette threadpool\nstall when Ollama is slow.\n\nDefault response: the refreshed ``PostOut`` (with the new\n``summary_md`` and ``summary_generated_at``). 502 when Ollama is\nunavailable — in that case the post's existing summary is left\nuntouched.\n\n``?debug=true`` (moderator-only): returns\n``{\"post\": PostOut, \"debug\": {\"prompt\", \"raw_llm_output\", \"model\",\n\"num_ctx\", \"duration_ms\"}}`` so editors can iterate on the prompt\nagainst real posts. Non-moderators passing ``debug=true`` are\nrejected with 403 (the debug payload reveals model + prompt + raw\noutput, which are internal). API-key callers must also be moderator\nrole — auth method doesn't matter, role does.","operationId":"generate_summary_api_posts__post_id__generate_summary_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"debug","in":"query","required":false,"schema":{"type":"boolean","description":"When true, return the prompt + raw LLM output + provider metadata alongside the post. Moderator-only — exposes internal prompt details.","default":false,"title":"Debug"},"description":"When true, return the prompt + raw LLM output + provider metadata alongside the post. Moderator-only — exposes internal prompt details."},{"name":"mode","in":"query","required":false,"schema":{"enum":["auto","refresh","rebuild"],"type":"string","description":"Regen mode. 'refresh': use prior summary as starting point. 'rebuild': treat prior summary as reference only, build from scratch. 'auto' (default): detect at request time — if any comment is newer than summary_generated_at, use 'rebuild'; otherwise 'refresh'. Posts with no prior summary always use 'rebuild'.","default":"auto","title":"Mode"},"description":"Regen mode. 'refresh': use prior summary as starting point. 'rebuild': treat prior summary as reference only, build from scratch. 'auto' (default): detect at request time — if any comment is newer than summary_generated_at, use 'rebuild'; otherwise 'refresh'. Posts with no prior summary always use 'rebuild'."},{"name":"preserve_sections","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated list of section-heading substrings or comment-id prefixes (c-XXXXXX) that MUST appear in the new summary verbatim or near-verbatim. Empty/missing = current behaviour. Max 20 tokens, each max 80 chars.","title":"Preserve Sections"},"description":"Comma-separated list of section-heading substrings or comment-id prefixes (c-XXXXXX) that MUST appear in the new summary verbatim or near-verbatim. Empty/missing = current behaviour. Max 20 tokens, each max 80 chars."}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/GenerateSummaryBody"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary":{"put":{"tags":["posts"],"summary":"Put Summary","description":"Write a BYOT-generated summary to a post.\n\nAuth: originator OR global moderator OR post-level moderator.  Same gate\nas ``POST /generate-summary``.\n\nOptimistic-concurrency check: ``body.context_etag`` must match the current\nsha256 of the summary-context payload.  If the post changed (new comments,\nprior summary regenerated) between GET and PUT, the etag mismatches and the\nendpoint returns 409 so the agent can re-GET and retry.\n\nValidator pipeline runs before any write:\n1. Length check (> 5500 chars → 422).\n2. Citation strip (removes hallucinated [c-XXXXXX] IDs; informational).\n3. Citation existence (belt-and-braces orphan check; informational).\n4. Topic-drift guard (first-sentence token overlap; blocking → 422).\n5. Schema-as-contract (≥60% schema axes present; blocking → 422).\n\nOn success:\n- Prior summary_md is snapshotted to post_summary_history.\n- post.summary_md, post.summary_generated_at, post.summary_byot_model are\n  updated in a single atomic commit.\n- Audit event ``agent.summary.byot_write`` is emitted.\n\nResponse: {post: PostOut, byot_meta: {...}} including stripped citation IDs\nand the topic-drift score for operator visibility.","operationId":"put_summary_api_posts__post_id__summary_put","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ByotSummaryBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary-diff":{"get":{"tags":["posts"],"summary":"Get Post Summary Diff","description":"Compare the current topic-page summary against a prior version.\n\nPublic read — no auth required (matches ``GET /api/posts/{id}`` read\npattern).\n\n``?previous=N`` (default 1) selects the N-th most recent snapshot from\n``post_summary_history``. Snapshots are written every time\n``POST /api/posts/{id}/generate-summary`` succeeds and there was an\nexisting summary to preserve.\n\nReturns a structured diff with section-level changes and citation-set\ndeltas.  404 when no prior history exists for the post or when the\nrequested N-th version doesn't exist.","operationId":"get_post_summary_diff_api_posts__post_id__summary_diff_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"previous","in":"query","required":false,"schema":{"type":"integer","maximum":10,"minimum":1,"description":"N-th most recent prior version to diff against (1 = latest prior, 2 = second-most-recent, …). Range: 1–10.","default":1,"title":"Previous"},"description":"N-th most recent prior version to diff against (1 = latest prior, 2 = second-most-recent, …). Range: 1–10."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary-candidate/qwen-check":{"post":{"tags":["posts"],"summary":"Qwen Check Summary Candidate","description":"Run an on-demand qwen guard check on the pending summary candidate.\n\nAuth: originator OR global moderator OR post-level moderator.\nBearer callers must have MODERATE scope.\n\nRuns ``check_topic_drift`` + ``check_content_appropriateness`` sequentially\nunder the shared qwen-guard semaphore (blocking — on-demand, not opportunistic).\nWrites one ``QwenGuardCheck`` row per call and applies a ``CommentMark`` on the\n``summary_candidate`` subject when the outcome is deterministic:\n  - drift=anchored AND appropriateness=appropriate → mark=\"approved\"\n  - drift=drifted OR appropriateness=inappropriate  → mark=\"disapproved\"\n  - anything else (partial, concerning, editorial_concern)   → no mark (mod decides)\n\nReturns::\n\n    {\n      \"post_id\": \"...\",\n      \"drift\": {\"verdict\": \"anchored|partial|drifted\", \"score\": 0.78, \"evidence\": \"...\"},\n      \"appropriateness\": {\"verdict\": \"appropriate|concerning|editorial_concern|inappropriate\",\n                          \"evidence\": \"...\"},\n      \"mark_applied\": \"approved | disapproved | null\",\n      \"ran_at\": \"...\"\n    }\n\n400 when no candidate exists.\n502 when Ollama is unreachable (no rows written, no marks applied).\n503 when the qwen semaphore cannot be acquired within 60s.","operationId":"qwen_check_summary_candidate_api_posts__post_id__summary_candidate_qwen_check_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary/promote":{"post":{"tags":["posts"],"summary":"Promote Summary Candidate","description":"Promote ``post.summary_candidate_md`` → ``post.summary_md``.\n\nAuth: originator OR global moderator OR post-level moderator (per WS-B spec).\n\nRequires a qwen approval mark on the candidate\n(subject_type=\"summary_candidate\", subject_id=post_id) unless\n``?override=true`` is set.  On success, the prior canonical is\nsnapshotted to ``post_summary_history``, the candidate columns are\ncopied to the canonical columns and cleared, and audit event\n``summary.canonical.promoted`` is emitted.\n\n409 when no candidate exists.\n409 when qwen approval mark is absent (unless override=true).","operationId":"promote_summary_candidate_api_posts__post_id__summary_promote_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"override","in":"query","required":false,"schema":{"type":"boolean","description":"When true, promote even without a qwen approval mark. The waiver is audit-logged. Moderator-only.","default":false,"title":"Override"},"description":"When true, promote even without a qwen approval mark. The waiver is audit-logged. Moderator-only."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary/reject-candidate":{"post":{"tags":["posts"],"summary":"Reject Summary Candidate","description":"Discard the pending summary candidate for a post.\n\nAuth: global moderator only.\n\nClears ``summary_candidate_md`` (and related columns) without\ntouching the live canonical ``summary_md``. Emits audit event\n``summary.candidate.rejected``.\n\n409 when no candidate exists.","operationId":"reject_summary_candidate_api_posts__post_id__summary_reject_candidate_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary/history":{"get":{"tags":["posts"],"summary":"Get Summary History","description":"Return summary version history for a post.\n\nReturns all ``post_summary_history`` rows (most recent first) plus\nthe current candidate if any.  Public read — same visibility as\n``GET /api/posts/{id}``.\n\nResponse::\n\n    {\n      \"post_id\": \"...\",\n      \"canonical_summary_md\": \"...\",\n      \"candidate\": {\n        \"summary_md\": \"...\",\n        \"generated_at\": \"...\",\n        \"byot_model\": \"...\"\n      } | null,\n      \"history\": [\n        {\n          \"id\": \"...\",\n          \"summary_md\": \"...\",\n          \"generated_at\": \"...\",\n          \"saved_at\": \"...\"\n        },\n        ...\n      ]\n    }","operationId":"get_summary_history_api_posts__post_id__summary_history_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/references":{"get":{"tags":["posts"],"summary":"Get Post References","description":"Extract and liveness-check all references in a post's topic summary.\n\nAuth: originator OR global moderator OR post-level moderator.\nSame gate as ``GET /summary-context``.\n\nThe ``source`` query param controls which markdown is scanned:\n\n- ``canonical`` (default) — extracts from ``summary_md``. Backwards-compatible.\n- ``candidate`` — extracts from ``summary_candidate_md``. Returns 404 when\n  no candidate is present.\n- ``both`` — returns two labelled sets plus a diff so moderators can see\n  exactly which URLs are being added/removed on promotion.\n\nDefault response shape (``canonical`` or ``candidate``)::\n\n    {\n      \"post_id\": \"...\",\n      \"references\": [...],\n      \"counts\": {\"alive\": 5, \"uncheckable\": 2, \"dead\": 1, \"unlinked\": 3}\n    }\n\nResponse shape when ``source=both``::\n\n    {\n      \"post_id\": \"...\",\n      \"canonical\": {\"references\": [...], \"counts\": {...}},\n      \"candidate\": {\"references\": [...], \"counts\": {...}} | null,\n      \"diff\": {\n        \"urls_added\": [...],   // in candidate but not canonical\n        \"urls_removed\": [...], // in canonical but not candidate\n        \"urls_kept\": [...]\n      }\n    }","operationId":"get_post_references_api_posts__post_id__references_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"source","in":"query","required":false,"schema":{"type":"string","default":"canonical","title":"Source"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/summary/flag":{"post":{"tags":["posts"],"summary":"Flag Summary","description":"Reader-feedback: flag the topic-page summary as potentially wrong.\n\nAuth: authenticated (session or API key).  Design decision: auth-required\nbecause ``RateLimitBucket`` is keyed on ``user_id`` (FK NOT NULL) — adding\nIP-hash rate limiting would need a new table.  The auth requirement also\nraises the floor for noise without meaningfully blocking real readers.\n\nRate limit: 10 flags per user per day.  The audience is readers noticing\ngenuine drift, not moderators triaging in bulk; 10/day is generous.\n\nBehavior:\n1. Validate post exists (404 if not).\n2. Rate-limit per user (10/day).\n3. Create a ``Comment`` row with ``kind='flag'`` / ``state='published'``\n   carrying a structured body_md.  The ``author_id`` is the authenticated\n   viewer.\n4. Emit audit event ``summary.flag.reader_feedback``.\n5. Return 201 ``{flagged: true, flag_comment_id: str}``.","operationId":"flag_summary_api_posts__post_id__summary_flag_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SummaryFlagRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/freeze-as-essay":{"post":{"tags":["posts"],"summary":"Freeze As Essay","description":"Freeze the current ``summary_md`` to a new ``essays/<slug>.md``.\n\nAuth: moderator only. Even when the originator is treated as topic\nadmin elsewhere, this writes to canon-on-disk so we gate hard on the\nmoderator role.\n\n409 when ``post.summary_md`` is null — generate first, then freeze.\nEmits ``post.essay.frozen`` to the audit chain.","operationId":"freeze_as_essay_api_posts__post_id__freeze_as_essay_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/FreezeAsEssayBody"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FreezeAsEssayOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/moderators":{"get":{"tags":["posts"],"summary":"List Post Moderators","description":"List post-level moderators for a post.\n\nAnonymous read (originator is implicit and exposed via ``author`` on\nthe main post payload; this endpoint surfaces only the explicit\n``post_moderator`` rows). Useful for the topic-page UI to render\nco-mod chips and for an originator to inspect their ACL.","operationId":"list_post_moderators_api_posts__post_id__moderators_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostModeratorList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["posts"],"summary":"Add Post Moderator","description":"Promote a user to post-level moderator.\n\nAuth: originator OR existing post-mod OR global moderator/admin.\n\"Any mod can do mod things\" — co-mods can promote others; matches\nthe comments-surface pattern.\n\n409 if the target is the originator (they're already implicit\nadmin). 409 if the target is already a post-mod (idempotency\ndeliberately not silent — callers should know the row already\nexists rather than silently no-op). Audit event\n``post.moderator.added``.","operationId":"add_post_moderator_api_posts__post_id__moderators_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostModeratorCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostModeratorOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/moderators/{user_id}":{"delete":{"tags":["posts"],"summary":"Remove Post Moderator","description":"Revoke post-level moderator authority.\n\nAuth: originator OR global moderator/admin only. Post-mods cannot\nrevoke other post-mods — revocation is reserved to the originator\n(and to global moderators in case of abuse). 403 with an explicit\nerror if asked to remove the originator (they're not in this\ntable; making the error explicit prevents silent misuse).\n\n404 if no such row exists. Audit event ``post.moderator.removed``.","operationId":"remove_post_moderator_api_posts__post_id__moderators__user_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/submit":{"post":{"tags":["posts"],"summary":"Submit Post","operationId":"submit_post_api_posts__post_id__submit_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/useful":{"post":{"tags":["posts"],"summary":"Toggle Useful","description":"Toggle a \"useful\" mark on a published post.\n\nIdempotent toggle: inserts a row in ``post_useful_mark`` for\n``(post_id, viewer.id)`` if absent, deletes it if present. Recomputes\nthe denormalised ``post.useful_count`` from the mark table so the\ncounter self-heals if it ever drifts.\n\nAuth: session OR API key (matches the comments/reactions surface).\nOnly ``published`` posts accept marks — every other state refuses\nwith 403 so users can't pre-mark drafts or hoard signal on\nretracted/archived rows.\n\nRate-limited at 60/min per user, same bucket as comment reactions.\nNo audit event — high-volume user activity, and the mark table\nitself is the audit trail.","operationId":"toggle_useful_api_posts__post_id__useful_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostUsefulOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/essay":{"get":{"tags":["post-essay"],"summary":"Get Essay","operationId":"get_essay_api_posts__post_id__essay_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EssayOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["post-essay"],"summary":"Put Essay","operationId":"put_essay_api_posts__post_id__essay_put","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"If-Match","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"If-Match"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EssayPutBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EssayOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/admins":{"get":{"tags":["post-essay"],"summary":"Get Admins","operationId":"get_admins_api_posts__post_id__admins_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["post-essay"],"summary":"Grant Co Admin","operationId":"grant_co_admin_api_posts__post_id__admins_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantCoAdminBody"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminsOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/admins/{user_id}":{"delete":{"tags":["post-essay"],"summary":"Revoke Co Admin","operationId":"revoke_co_admin_api_posts__post_id__admins__user_id__delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/essay/votes":{"get":{"tags":["post-essay"],"summary":"Get Votes","operationId":"get_votes_api_posts__post_id__essay_votes_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoteOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/essay/vote":{"post":{"tags":["post-essay"],"summary":"Vote","operationId":"vote_api_posts__post_id__essay_vote_post","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VoteOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["post-essay"],"summary":"Unvote","operationId":"unvote_api_posts__post_id__essay_vote_delete","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/users/{key}":{"get":{"tags":["users"],"summary":"Get User","description":"Resolve ``key`` as a handle first, then as a UUID for back-compat.\n\nHandle lookup is case-insensitive; UUID lookup goes through the\nprimary key. 404 when neither matches.","operationId":"get_user_api_users__key__get","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPublicOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications":{"get":{"tags":["notifications"],"summary":"List Notifications","operationId":"list_notifications_api_notifications_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(all|mentions|system|unread)?$"},{"type":"null"}],"title":"Filter"}},{"name":"include_dismissed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Dismissed"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__notification_schemas__NotificationList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/unread":{"get":{"tags":["notifications"],"summary":"Unread Count","operationId":"unread_count_api_notifications_unread_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadOut"}}}}}}},"/api/notifications/mark-all-read":{"post":{"tags":["notifications"],"summary":"Mark All Read","operationId":"mark_all_read_api_notifications_mark_all_read_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Read","operationId":"mark_read_api_notifications__notification_id__read_post","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Notification Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/notifications/{notification_id}":{"delete":{"tags":["notifications"],"summary":"Dismiss","operationId":"dismiss_api_notifications__notification_id__delete","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Notification Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/notifications":{"get":{"tags":["notifications"],"summary":"List Notifications","operationId":"list_notifications_api_me_notifications_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"unread_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Unread Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__routers__me_notifications__NotificationList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/notifications/unread_count":{"get":{"tags":["notifications"],"summary":"Unread Count","description":"Lean endpoint for NotifBell polling — single COUNT query.","operationId":"unread_count_api_me_notifications_unread_count_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnreadCountOut"}}}}}}},"/api/me/notifications/mark_all_read":{"post":{"tags":["notifications"],"summary":"Mark All Read","operationId":"mark_all_read_api_me_notifications_mark_all_read_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkAllReadOut"}}}}}}},"/api/me/notifications/{notification_id}/read":{"post":{"tags":["notifications"],"summary":"Mark Read","operationId":"mark_read_api_me_notifications__notification_id__read_post","parameters":[{"name":"notification_id","in":"path","required":true,"schema":{"type":"string","title":"Notification Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarkReadOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/videos":{"get":{"tags":["videos"],"summary":"List Videos","operationId":"list_videos_api_videos_get","parameters":[{"name":"channel","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by channel ID","title":"Channel"},"description":"Filter by channel ID"},{"name":"tag","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by tag","title":"Tag"},"description":"Filter by tag"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"highlight","in":"query","required":false,"schema":{"type":"boolean","description":"Only return front-page highlight videos","default":false,"title":"Highlight"},"description":"Only return front-page highlight videos"},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only videos on or after this date (YYYY-MM-DD)","title":"Since"},"description":"Only videos on or after this date (YYYY-MM-DD)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/videos/{video_id}":{"get":{"tags":["videos"],"summary":"Get Video","operationId":"get_video_api_videos__video_id__get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Agent"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/videos/{video_id}/template-fill":{"put":{"tags":["videos"],"summary":"Put Template Fill","description":"BYOT writeback. Auth: session+CSRF+role≥moderator OR bearer+moderate.\n\nValidates slots against the template, renders the body, stores as\n``drafted``. Returns the new draft (or 422 with slot-level errors).","operationId":"put_template_fill_api_videos__video_id__template_fill_put","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TemplateFillPut"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/channels":{"get":{"tags":["channels"],"summary":"List Channels","operationId":"list_channels_api_channels_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/channels/{channel_id}":{"get":{"tags":["channels"],"summary":"Get Channel","operationId":"get_channel_api_channels__channel_id__get","parameters":[{"name":"channel_id","in":"path","required":true,"schema":{"type":"string","title":"Channel Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays":{"get":{"tags":["essays"],"summary":"List Essays","operationId":"list_essays_api_essays_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays/{slug}/thumbnail":{"get":{"tags":["essays"],"summary":"Get Essay Thumbnail","operationId":"get_essay_thumbnail_api_essays__slug__thumbnail_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays/{slug}":{"get":{"tags":["essays"],"summary":"Get Essay","operationId":"get_essay_api_essays__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Agent"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/tags":{"get":{"tags":["tags"],"summary":"List Tags","operationId":"list_tags_api_tags_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/digests":{"get":{"tags":["digests"],"summary":"List Digests","operationId":"list_digests_api_digests_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only digests on or after this date (YYYY-MM-DD)","title":"Since"},"description":"Only digests on or after this date (YYYY-MM-DD)"},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/digests/{date}/thumbnail":{"get":{"tags":["digests"],"summary":"Get Digest Thumbnail","operationId":"get_digest_thumbnail_api_digests__date__thumbnail_get","parameters":[{"name":"date","in":"path","required":true,"schema":{"type":"string","title":"Date"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/digests/{date}":{"get":{"tags":["digests"],"summary":"Get Digest","operationId":"get_digest_api_digests__date__get","parameters":[{"name":"date","in":"path","required":true,"schema":{"type":"string","title":"Date"}},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Agent"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/personas":{"get":{"tags":["personas"],"summary":"List Personas","operationId":"list_personas_api_personas_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/personas/modelfiles/{persona_id}/{mode}/{lang}":{"get":{"tags":["personas"],"summary":"Get Modelfile","operationId":"get_modelfile_api_personas_modelfiles__persona_id___mode___lang__get","parameters":[{"name":"persona_id","in":"path","required":true,"schema":{"type":"string","title":"Persona Id"}},{"name":"mode","in":"path","required":true,"schema":{"type":"string","title":"Mode"}},{"name":"lang","in":"path","required":true,"schema":{"type":"string","title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/personas/{persona_id}":{"get":{"tags":["personas"],"summary":"Get Persona","operationId":"get_persona_api_personas__persona_id__get","parameters":[{"name":"persona_id","in":"path","required":true,"schema":{"type":"string","title":"Persona Id"}},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Agent"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/knowledge":{"get":{"tags":["knowledge"],"summary":"List Knowledge","operationId":"list_knowledge_api_knowledge_get","parameters":[{"name":"draft","in":"query","required":false,"schema":{"type":"boolean","description":"Include draft concepts in listing","default":false,"title":"Draft"},"description":"Include draft concepts in listing"},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/knowledge/{slug}":{"get":{"tags":["knowledge"],"summary":"Get Knowledge","operationId":"get_knowledge_api_knowledge__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Agent"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2,"pattern":"^[a-z]{2}$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/search":{"get":{"tags":["search"],"summary":"Search Content","operationId":"search_content_api_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":1,"description":"Search query","title":"Q"},"description":"Search query"},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"DEPRECATED — single content type filter. Prefer `types`.","title":"Type"},"description":"DEPRECATED — single content type filter. Prefer `types`."},{"name":"types","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated content types: video,knowledge,essay,digest,persona,post","title":"Types"},"description":"Comma-separated content types: video,knowledge,essay,digest,persona,post"},{"name":"hybrid","in":"query","required":false,"schema":{"type":"boolean","description":"Fuse keyword + vector results via RRF. Set false for legacy keyword-only.","default":true,"title":"Hybrid"},"description":"Fuse keyword + vector results via RRF. Set false for legacy keyword-only."},{"name":"draft","in":"query","required":false,"schema":{"type":"boolean","description":"Include draft canon (concept_stub proposals) in results. When true, draft hits are included with a 0.7 score multiplier. Default false.","default":false,"title":"Draft"},"description":"Include draft canon (concept_stub proposals) in results. When true, draft hits are included with a 0.7 score multiplier. Default false."},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[a-z]{2}$"},{"type":"null"}],"description":"ISO 639-1 language hint. Vector hits in a different language get a 0.8 score multiplier (same-lang results float to the top without hiding cross-lingual matches). No-op when set to None.","title":"Lang"},"description":"ISO 639-1 language hint. Vector hits in a different language get a 0.8 score multiplier (same-lang results float to the top without hiding cross-lingual matches). No-op when set to None."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results","default":20,"title":"Limit"},"description":"Max results"},{"name":"agent","in":"query","required":false,"schema":{"type":"boolean","description":"Rewrite links for agent consumption","default":false,"title":"Agent"},"description":"Rewrite links for agent consumption"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/videos/{video_id}/related":{"get":{"tags":["related"],"summary":"Video Related","operationId":"video_related_api_videos__video_id__related_get","parameters":[{"name":"video_id","in":"path","required":true,"schema":{"type":"string","title":"Video Id"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/knowledge/{slug}/related":{"get":{"tags":["related"],"summary":"Knowledge Related","operationId":"knowledge_related_api_knowledge__slug__related_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/essays/{slug}/related":{"get":{"tags":["related"],"summary":"Essay Related","operationId":"essay_related_api_essays__slug__related_get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/digests/{date}/related":{"get":{"tags":["related"],"summary":"Digest Related","operationId":"digest_related_api_digests__date__related_get","parameters":[{"name":"date","in":"path","required":true,"schema":{"type":"string","title":"Date"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/personas/{persona_id}/related":{"get":{"tags":["related"],"summary":"Persona Related","operationId":"persona_related_api_personas__persona_id__related_get","parameters":[{"name":"persona_id","in":"path","required":true,"schema":{"type":"string","title":"Persona Id"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/posts/{post_id}/related":{"get":{"tags":["related"],"summary":"Post Related","operationId":"post_related_api_posts__post_id__related_get","parameters":[{"name":"post_id","in":"path","required":true,"schema":{"type":"string","title":"Post Id"}},{"name":"k","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"K"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chat/auth":{"post":{"tags":["chat"],"summary":"Authenticate","operationId":"authenticate_api_chat_auth_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuthRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/chat/completions":{"post":{"tags":["chat"],"summary":"Chat Completions","operationId":"chat_completions_api_chat_completions_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChatRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sources":{"post":{"tags":["sources"],"summary":"Add Source","operationId":"add_source_api_sources_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddSourceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["sources"],"summary":"List Sources","description":"Paginated listing of every source-of-truth entry, most-recent-first.\n\nKnowledge concepts are intentionally excluded (they are derived from\nsources, not sources themselves). Drafts (posts, knowledge) and\ncomments below the top level are also excluded.","operationId":"list_sources_api_sources_get","parameters":[{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated subset of: video,essay,digest,web_page,post,comment","title":"Type"},"description":"Comma-separated subset of: video,essay,digest,web_page,post,comment"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/sources/knowledge":{"post":{"tags":["sources"],"summary":"Add Knowledge","description":"Create a knowledge concept page from a topic. Uses AI to generate content.","operationId":"add_knowledge_api_sources_knowledge_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddKnowledgeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/graph":{"get":{"tags":["graph"],"summary":"Get Graph Html","description":"Serve the interactive knowledge graph visualization.","operationId":"get_graph_html_api_graph_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api/graph/data":{"get":{"tags":["graph"],"summary":"Get Graph Data","description":"Return the raw graph data (nodes + edges) as JSON.","operationId":"get_graph_data_api_graph_data_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/audit":{"get":{"tags":["audit"],"summary":"List Events","operationId":"list_events_api_audit_get","parameters":[{"name":"after_seq","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"After Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kind"}},{"name":"subject_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject Type"}},{"name":"subject_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit/checkpoints":{"get":{"tags":["audit"],"summary":"List Checkpoints","operationId":"list_checkpoints_api_audit_checkpoints_get","parameters":[{"name":"after_seq","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"After Seq"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditCheckpointPage"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/audit/pubkey":{"get":{"tags":["audit"],"summary":"Get Audit Pubkey","operationId":"get_audit_pubkey_api_audit_pubkey_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditPubkeyOut"}}}}}}},"/api/auth/github/begin":{"get":{"tags":["personal-knowledge"],"summary":"Begin","description":"Return the GitHub OAuth authorize URL the frontend should redirect to.","operationId":"begin_api_auth_github_begin_get","parameters":[{"name":"redirect_uri","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Uri"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/auth/github/callback":{"get":{"tags":["personal-knowledge"],"summary":"Callback","description":"Handle GitHub's OAuth callback. On success, 302-redirects the browser\nback to ``/account?pk=connected`` where the SPA toasts and refreshes.","operationId":"callback_api_auth_github_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Description"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/personal-knowledge":{"get":{"tags":["personal-knowledge"],"summary":"Get Status","operationId":"get_status_api_me_personal_knowledge_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Get Status Api Me Personal Knowledge Get"}}}}}},"delete":{"tags":["personal-knowledge"],"summary":"Disconnect Backend","operationId":"disconnect_backend_api_me_personal_knowledge_delete","responses":{"204":{"description":"Successful Response"}}}},"/api/me/personal-knowledge/notes":{"get":{"tags":["personal-knowledge"],"summary":"List Notes","operationId":"list_notes_api_me_personal_knowledge_notes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response List Notes Api Me Personal Knowledge Notes Get"}}}}}}},"/api/me/personal-knowledge/notes/{slug}":{"get":{"tags":["personal-knowledge"],"summary":"Get Note","operationId":"get_note_api_me_personal_knowledge_notes__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":80,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["personal-knowledge"],"summary":"Put Note","operationId":"put_note_api_me_personal_knowledge_notes__slug__put","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":80,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Put Note Api Me Personal Knowledge Notes  Slug  Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["personal-knowledge"],"summary":"Delete Note","operationId":"delete_note_api_me_personal_knowledge_notes__slug__delete","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":80,"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Note Api Me Personal Knowledge Notes  Slug  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/me/personal-knowledge/history":{"get":{"tags":["personal-knowledge"],"summary":"Get History","description":"Return up to ``limit`` history entries (newest first).\n\nPass ``?slug=...`` to scope the history to one note. Without a slug,\nbackends return a recent-activity feed (best-effort per backend).","operationId":"get_history_api_me_personal_knowledge_history_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"title":"Response Get History Api Me Personal Knowledge History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/submit":{"post":{"tags":["meter"],"summary":"Submit Meter","operationId":"submit_meter_api_meter_submit_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterSubmitRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/heatmap":{"get":{"tags":["meter"],"summary":"Get Heatmap","operationId":"get_heatmap_api_meter_heatmap_get","parameters":[{"name":"since","in":"query","required":false,"schema":{"type":"string","pattern":"^(30d|90d|all)$","default":"30d","title":"Since"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterHeatmapResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/takes/mine":{"get":{"tags":["meter"],"summary":"List My Takes","operationId":"list_my_takes_api_meter_takes_mine_get","parameters":[{"name":"anon_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9]{22}$"},{"type":"null"}],"title":"Anon Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterTakesMineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["meter"],"summary":"Delete My Takes","operationId":"delete_my_takes_api_meter_takes_mine_delete","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterDeleteRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/takes/{take_id}/opt-in":{"patch":{"tags":["meter"],"summary":"Patch Opt In","operationId":"patch_opt_in_api_meter_takes__take_id__opt_in_patch","parameters":[{"name":"take_id","in":"path","required":true,"schema":{"type":"string","title":"Take Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OptInBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeterTakeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/og/{take_id}.png":{"get":{"tags":["meter"],"summary":"Meter Og Card By Int","description":"Legacy integer-keyed OG card endpoint.\n\nStill wired so social-network posts cached against the old URL keep\nrendering. Every hit writes a ``meter.share.legacy_int_lookup`` audit\nevent so we can measure when to retire the route.","operationId":"meter_og_card_by_int_api_meter_og__take_id__png_get","parameters":[{"name":"take_id","in":"path","required":true,"schema":{"type":"integer","minimum":1,"title":"Take Id"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|sl)$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"image/png":{}}},"404":{"description":"Take not found or not opted in to sharing"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/meter/og/{token}.png":{"get":{"tags":["meter"],"summary":"Get Og Card By Token","description":"Return the 1200x630 Open Graph share card for ``token``.\n\nCanonical surface — keys takes by an opaque 12-char URL-safe nonce\nso the share endpoint can't be enumerated by counting integer IDs.","operationId":"get_og_card_by_token_api_meter_og__token__png_get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","minLength":8,"maxLength":16,"pattern":"^[A-Za-z0-9_-]{8,16}$","title":"Token"}},{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(en|sl)$"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"image/png":{}}},"404":{"description":"Take not found or not opted in to sharing"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/templates":{"get":{"tags":["templates"],"summary":"List Templates Endpoint","description":"Cheap list — id, version, display name, description. Cacheable.","operationId":"list_templates_endpoint_api_templates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/templates/{template_id}":{"get":{"tags":["templates"],"summary":"Get Template Endpoint","description":"Full template definition + ETag + Cache-Control.","operationId":"get_template_endpoint_api_templates__template_id__get","parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/topics":{"get":{"tags":["topics"],"summary":"List Topics","description":"Return published posts that have a topic summary and sufficient community activity.\n\nQuality gate (all must pass):\n- ``post.state == \"published\"``\n- ``post.summary_md IS NOT NULL``\n- Published comment count for the post >= 3\n- Distinct published comment author count for the post >= 2\n\nSorted by ``summary_generated_at DESC``.","operationId":"list_topics_api_topics_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":10,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TopicSummaryList"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api":{"get":{"tags":["agent"],"summary":"Api Root","description":"API entry point. Returns SKILL.md for agents, or a brief JSON summary with latest content for browsers.","operationId":"api_root_api_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/latest":{"get":{"tags":["agent"],"summary":"Get Latest","description":"One-call briefing: latest digest + recent videos + essays + knowledge stats.\n\nDesigned for agents that just want to fetch one URL and get useful content.\n`/api/brief` is an alias — some agents intuit this URL first.\n\n``lang`` is an optional ISO 639-1 hint applied to essay titles/\ndescriptions (canonical English otherwise). Bad shapes are silently\ncoerced to None to avoid breaking /api/latest — this endpoint must\nnever 4xx on an agent's behalf when there's anything sensible to\nreturn.","operationId":"get_latest_api_latest_get","parameters":[{"name":"lang","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Lang"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/skill.md":{"get":{"tags":["agent"],"summary":"Get Skill Md","operationId":"get_skill_md_api_skill_md_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/skills":{"get":{"tags":["agent"],"summary":"List Skills","description":"List Claude Code skills installable via the API. These work without a local repo clone.","operationId":"list_skills_api_skills_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/skills/{name}":{"get":{"tags":["agent"],"summary":"Get Skill","description":"Serve a single skill file as plain text markdown.","operationId":"get_skill_api_skills__name__get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/config":{"get":{"tags":["config"],"summary":"Get Config","description":"Public client-side config.\n\n``dev_mode`` previously lived on ``/api/health``; it surfaces here now\nthat ``/api/health`` is a minimal liveness probe. Used by the login UI\nto decide whether to render the local dev-login form. Not a secret on\neither side of the env split.\n\n``comment_max_chars``, ``post_body_max_chars``, ``summary_max_chars`` —\noperator-configurable length caps (env: APP_COMMENT_MAX_CHARS,\nAPP_POST_BODY_MAX_CHARS, APP_SUMMARY_MAX_CHARS). The frontend reads\nthese once and wires them to CharCounter / submission guards so the UI\nand API stay in sync without a deploy.","operationId":"get_config_api_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/health":{"get":{"tags":["health"],"summary":"Health Check","description":"Minimal liveness probe.\n\nAnonymous, no operational detail — used by load balancers, the Docker\nHEALTHCHECK, and agents that just want to know \"is the site up?\".\nDetailed operational state (paths, providers, bot wiring) lives behind\nthe admin-gated ``GET /api/dashboard/health`` endpoint.","operationId":"health_check_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AccountSetupRequest":{"properties":{"handle":{"type":"string","maxLength":30,"minLength":3,"title":"Handle"},"nickname":{"type":"string","maxLength":60,"minLength":1,"title":"Nickname"}},"type":"object","required":["handle","nickname"],"title":"AccountSetupRequest","description":"Body for ``POST /api/account/setup`` — first-login choose-handle."},"AddKnowledgeRequest":{"properties":{"topic":{"type":"string","title":"Topic"},"password":{"type":"string","title":"Password","default":""}},"type":"object","required":["topic"],"title":"AddKnowledgeRequest"},"AddSourceRequest":{"properties":{"url":{"type":"string","title":"Url"},"password":{"type":"string","title":"Password","default":""}},"type":"object","required":["url"],"title":"AddSourceRequest"},"AdminUserList":{"properties":{"users":{"items":{"$ref":"#/components/schemas/AdminUserOut"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["users","total"],"title":"AdminUserList"},"AdminUserOut":{"properties":{"id":{"type":"string","title":"Id"},"email":{"type":"string","title":"Email"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nickname"},"display_name":{"type":"string","title":"Display Name"},"role":{"$ref":"#/components/schemas/UserRole"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","email","display_name","role","created_at"],"title":"AdminUserOut"},"AdminsOut":{"properties":{"creator":{"$ref":"#/components/schemas/_UserBrief"},"co_admins":{"items":{"$ref":"#/components/schemas/CoAdminOut"},"type":"array","title":"Co Admins"}},"type":"object","required":["creator","co_admins"],"title":"AdminsOut"},"ApiKeyCreate":{"properties":{"name":{"type":"string","maxLength":120,"minLength":1,"title":"Name"},"expires_in_days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days"},"expires_in_seconds":{"anyOf":[{"type":"integer","maximum":31536000.0,"minimum":60.0},{"type":"null"}],"title":"Expires In Seconds"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes"},"nickname_override":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Nickname Override"},"display_name_override":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Display Name Override"}},"type":"object","required":["name"],"title":"ApiKeyCreate"},"ApiKeyCreateResponse":{"properties":{"key":{"type":"string","title":"Key"},"id":{"type":"string","title":"Id"},"prefix":{"type":"string","title":"Prefix"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"nickname_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nickname Override"},"display_name_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name Override"}},"type":"object","required":["key","id","prefix"],"title":"ApiKeyCreateResponse"},"ApiKeyOut":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"prefix":{"type":"string","title":"Prefix"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes","default":[]},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"created_at":{"type":"string","title":"Created At"},"nickname_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nickname Override"},"display_name_override":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name Override"}},"type":"object","required":["id","name","prefix","created_at"],"title":"ApiKeyOut"},"AuditCheckpointOut":{"properties":{"id":{"type":"string","title":"Id"},"seq":{"type":"integer","title":"Seq"},"at":{"type":"string","title":"At"},"chain_hash":{"type":"string","title":"Chain Hash"},"signature":{"type":"string","title":"Signature"},"pubkey_id":{"type":"string","title":"Pubkey Id"}},"type":"object","required":["id","seq","at","chain_hash","signature","pubkey_id"],"title":"AuditCheckpointOut"},"AuditCheckpointPage":{"properties":{"checkpoints":{"items":{"$ref":"#/components/schemas/AuditCheckpointOut"},"type":"array","title":"Checkpoints"},"total_seq":{"type":"integer","title":"Total Seq"}},"type":"object","required":["checkpoints","total_seq"],"title":"AuditCheckpointPage"},"AuditEventOut":{"properties":{"seq":{"type":"integer","title":"Seq"},"at":{"type":"string","title":"At"},"kind":{"type":"string","title":"Kind"},"actor":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Actor"},"subject_type":{"type":"string","title":"Subject Type"},"subject_id":{"type":"string","title":"Subject Id"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"prev_hash":{"type":"string","title":"Prev Hash"},"record_hash":{"type":"string","title":"Record Hash"}},"type":"object","required":["seq","at","kind","subject_type","subject_id","payload","prev_hash","record_hash"],"title":"AuditEventOut"},"AuditPage":{"properties":{"events":{"items":{"$ref":"#/components/schemas/AuditEventOut"},"type":"array","title":"Events"},"total_seq":{"type":"integer","title":"Total Seq"}},"type":"object","required":["events","total_seq"],"title":"AuditPage"},"AuditPubkeyOut":{"properties":{"pubkey":{"type":"string","title":"Pubkey"},"pubkey_id":{"type":"string","title":"Pubkey Id"},"algorithm":{"type":"string","title":"Algorithm","default":"ed25519"},"openssh_pubkey":{"type":"string","title":"Openssh Pubkey"}},"type":"object","required":["pubkey","pubkey_id","openssh_pubkey"],"title":"AuditPubkeyOut"},"AuthRequest":{"properties":{"key":{"type":"string","title":"Key"}},"type":"object","required":["key"],"title":"AuthRequest"},"BrowsingContextItem":{"properties":{"type":{"type":"string","title":"Type"},"id":{"type":"string","title":"Id"},"path":{"type":"string","title":"Path"}},"type":"object","required":["type","id","path"],"title":"BrowsingContextItem"},"ByotSummaryBody":{"properties":{"summary_md":{"type":"string","maxLength":10000,"minLength":10,"title":"Summary Md"},"model":{"type":"string","maxLength":120,"minLength":1,"title":"Model"},"generated_at":{"type":"string","title":"Generated At","description":"ISO-8601 datetime when the agent generated this summary."},"context_etag":{"type":"string","maxLength":64,"minLength":1,"title":"Context Etag","description":"ETag from GET /summary-context — used for optimistic concurrency."}},"type":"object","required":["summary_md","model","generated_at","context_etag"],"title":"ByotSummaryBody","description":"Body for ``PUT /api/posts/{id}/summary`` (BYOT write path).\n\nThe agent generates the summary with its own model + key, then writes it\nback via this endpoint. The server runs the full validator pipeline before\npersisting."},"ChatMessage":{"properties":{"role":{"type":"string","title":"Role"},"content":{"type":"string","title":"Content"}},"type":"object","required":["role","content"],"title":"ChatMessage"},"ChatRequest":{"properties":{"key":{"type":"string","title":"Key"},"persona_id":{"type":"string","title":"Persona Id"},"mode":{"type":"string","title":"Mode","default":"persona"},"lang":{"type":"string","title":"Lang","default":"en"},"messages":{"items":{"$ref":"#/components/schemas/ChatMessage"},"type":"array","title":"Messages"},"browsing_context":{"items":{"$ref":"#/components/schemas/BrowsingContextItem"},"type":"array","title":"Browsing Context","default":[]}},"type":"object","required":["key","persona_id","messages"],"title":"ChatRequest"},"CoAdminOut":{"properties":{"id":{"type":"string","title":"Id"},"display_name":{"type":"string","title":"Display Name"},"granted_at":{"type":"string","title":"Granted At"},"granted_by_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Granted By Id"}},"type":"object","required":["id","display_name","granted_at"],"title":"CoAdminOut"},"CommentAuthor":{"properties":{"id":{"type":"string","title":"Id"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"type":"string","title":"Nickname"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"role":{"$ref":"#/components/schemas/UserRole"}},"type":"object","required":["id","nickname","role"],"title":"CommentAuthor","description":"Author projection on comment rows.\n\nUser-anonymity refactor: the canonical fields are ``handle`` +\n``nickname``. ``display_name`` is retained as a back-compat alias\npopulated with the same value as ``nickname`` for one deploy\ncycle so existing frontend renderers keep working. ``id`` (user\nUUID) stays for now because some frontend code still uses it for\navatar lookups."},"CommentCreate":{"properties":{"entity_type":{"type":"string","maxLength":32,"minLength":1,"title":"Entity Type"},"entity_id":{"type":"string","maxLength":255,"minLength":1,"title":"Entity Id"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"body_md":{"type":"string","maxLength":8000,"minLength":1,"title":"Body Md"},"attachments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Attachments"},"kind":{"type":"string","enum":["content","schema","artifact","watchlist"],"title":"Kind","default":"content"}},"type":"object","required":["entity_type","entity_id","body_md"],"title":"CommentCreate"},"CommentList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/CommentOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"CommentList"},"CommentMarkBody":{"properties":{"mark":{"type":"string","title":"Mark"},"evidence":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evidence"}},"type":"object","required":["mark"],"title":"CommentMarkBody","description":"Body for ``POST /api/comments/{id}/mark`` (moderator action)."},"CommentOut":{"properties":{"id":{"type":"string","title":"Id"},"entity_type":{"type":"string","title":"Entity Type"},"entity_id":{"type":"string","title":"Entity Id"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"thread_root_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Thread Root Id"},"author":{"$ref":"#/components/schemas/CommentAuthor"},"body_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Md"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html"},"attachments":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attachments","default":[]},"state":{"type":"string","title":"State"},"kind":{"type":"string","enum":["content","schema","artifact","watchlist"],"title":"Kind","default":"content"},"reaction_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reaction Counts","default":{}},"my_reactions":{"items":{"type":"string"},"type":"array","title":"My Reactions","default":[]},"edited_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Edited At"},"deleted_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deleted At"},"created_at":{"type":"string","title":"Created At"},"marked_for_deletion":{"type":"boolean","title":"Marked For Deletion","default":false}},"type":"object","required":["id","entity_type","entity_id","author","state","created_at"],"title":"CommentOut"},"CommentUpdate":{"properties":{"body_md":{"type":"string","maxLength":8000,"minLength":1,"title":"Body Md"},"attachments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Attachments"},"kind":{"anyOf":[{"type":"string","enum":["content","schema","artifact","watchlist"]},{"type":"null"}],"title":"Kind"}},"type":"object","required":["body_md"],"title":"CommentUpdate"},"CountOnly":{"properties":{"total":{"type":"integer","title":"Total"}},"type":"object","required":["total"],"title":"CountOnly"},"DashboardResponse":{"properties":{"stocks":{"$ref":"#/components/schemas/StocksResponse"},"flows":{"$ref":"#/components/schemas/FlowsResponse"},"schedules":{"$ref":"#/components/schemas/SchedulesResponse"},"subsystems":{"$ref":"#/components/schemas/SubsystemsResponse"}},"type":"object","required":["stocks","flows","schedules","subsystems"],"title":"DashboardResponse"},"DeviceApproveRequest":{"properties":{"user_code":{"type":"string","maxLength":24,"title":"User Code"},"decision":{"type":"string","pattern":"^(approve|deny)$","title":"Decision"}},"type":"object","required":["user_code","decision"],"title":"DeviceApproveRequest"},"DeviceAuthRequest":{"properties":{"client_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Client Name"},"client_host":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Client Host"}},"type":"object","title":"DeviceAuthRequest"},"DeviceLookupOut":{"properties":{"user_code":{"type":"string","title":"User Code"},"client_name":{"type":"string","title":"Client Name"},"client_host":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Host"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","title":"Created At"},"expires_at":{"type":"string","title":"Expires At"},"requested_scopes":{"items":{"type":"string"},"type":"array","title":"Requested Scopes"},"expires_in_days":{"type":"integer","title":"Expires In Days","default":90},"user_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Role"}},"type":"object","required":["user_code","client_name","status","created_at","expires_at"],"title":"DeviceLookupOut"},"DigestStocks":{"properties":{"total":{"type":"integer","title":"Total"},"latest_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Latest Date"}},"type":"object","required":["total"],"title":"DigestStocks"},"EmbeddingStocks":{"properties":{"target_types_covered":{"items":{"type":"string"},"type":"array","title":"Target Types Covered"},"total_embeddings":{"type":"integer","title":"Total Embeddings"},"coverage_percent":{"type":"integer","title":"Coverage Percent"}},"type":"object","required":["target_types_covered","total_embeddings","coverage_percent"],"title":"EmbeddingStocks"},"EssayEditor":{"properties":{"id":{"type":"string","title":"Id"},"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["id","display_name"],"title":"EssayEditor"},"EssayOut":{"properties":{"has_essay":{"type":"boolean","title":"Has Essay"},"body_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Md"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html"},"version":{"type":"integer","title":"Version","default":0},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"last_editor":{"anyOf":[{"$ref":"#/components/schemas/EssayEditor"},{"type":"null"}]}},"type":"object","required":["has_essay"],"title":"EssayOut"},"EssayPutBody":{"properties":{"body_md":{"type":"string","maxLength":80000,"minLength":1,"title":"Body Md"}},"type":"object","required":["body_md"],"title":"EssayPutBody"},"FlagCreate":{"properties":{"target_type":{"type":"string","maxLength":32,"minLength":1,"title":"Target Type"},"target_id":{"type":"string","maxLength":64,"minLength":1,"title":"Target Id"},"reason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reason"}},"type":"object","required":["target_type","target_id"],"title":"FlagCreate"},"FlowsByDay":{"properties":{"date":{"type":"string","title":"Date"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["date","count"],"title":"FlowsByDay"},"FlowsResponse":{"properties":{"since":{"type":"string","title":"Since"},"until":{"type":"string","title":"Until"},"total_events":{"type":"integer","title":"Total Events"},"by_kind":{"additionalProperties":true,"type":"object","title":"By Kind"},"by_day":{"items":{"$ref":"#/components/schemas/FlowsByDay"},"type":"array","title":"By Day"},"recent_kinds":{"items":{"type":"string"},"type":"array","title":"Recent Kinds"}},"type":"object","required":["since","until","total_events","by_kind","by_day","recent_kinds"],"title":"FlowsResponse"},"FreezeAsEssayBody":{"properties":{"featured":{"type":"boolean","title":"Featured","default":false}},"type":"object","title":"FreezeAsEssayBody","description":"Body for ``POST /api/posts/{id}/freeze-as-essay``.\n\n``featured`` writes ``featured: true`` into the resulting essay's\nfrontmatter, pinning it to the homepage's featured slot. Default\nfalse — moderators flip the flag explicitly."},"FreezeAsEssayOut":{"properties":{"essay_slug":{"type":"string","title":"Essay Slug"},"essay_path":{"type":"string","title":"Essay Path"},"featured":{"type":"boolean","title":"Featured"}},"type":"object","required":["essay_slug","essay_path","featured"],"title":"FreezeAsEssayOut"},"GenerateSummaryBody":{"properties":{},"type":"object","title":"GenerateSummaryBody","description":"Body for ``POST /api/posts/{id}/generate-summary``.\n\nReserved for future \"regenerate with a different style\" knobs. v1\naccepts no parameters — clients send ``{}`` or no body at all."},"GrantCoAdminBody":{"properties":{"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["user_id"],"title":"GrantCoAdminBody"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HandleCheckRequest":{"properties":{"handle":{"type":"string","maxLength":64,"minLength":1,"title":"Handle"}},"type":"object","required":["handle"],"title":"HandleCheckRequest","description":"Body for ``POST /api/account/check-handle``."},"HandleCheckResponse":{"properties":{"available":{"type":"boolean","title":"Available"},"normalized":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Normalized"},"suggestions":{"items":{"type":"string"},"type":"array","title":"Suggestions","default":[]},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["available"],"title":"HandleCheckResponse"},"HandleUpdate":{"properties":{"handle":{"type":"string","maxLength":30,"minLength":3,"title":"Handle"}},"type":"object","required":["handle"],"title":"HandleUpdate","description":"Body for ``PATCH /api/account/handle``."},"HealthCheck":{"properties":{"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"detail":{"type":"string","title":"Detail","default":""}},"type":"object","required":["name","status"],"title":"HealthCheck"},"JobOut":{"properties":{"id":{"type":"string","title":"Id"},"next_run_time":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Run Time"},"trigger":{"type":"string","title":"Trigger"},"description":{"type":"string","title":"Description"}},"type":"object","required":["id","trigger","description"],"title":"JobOut"},"KnowledgeStocks":{"properties":{"total":{"type":"integer","title":"Total"},"by_confidence":{"additionalProperties":true,"type":"object","title":"By Confidence"},"drafts":{"type":"integer","title":"Drafts"}},"type":"object","required":["total","by_confidence","drafts"],"title":"KnowledgeStocks"},"MarkAllReadOut":{"properties":{"marked":{"type":"integer","title":"Marked"}},"type":"object","required":["marked"],"title":"MarkAllReadOut"},"MarkReadOut":{"properties":{"id":{"type":"string","title":"Id"},"read_at":{"type":"string","title":"Read At"}},"type":"object","required":["id","read_at"],"title":"MarkReadOut"},"MeterAggregate":{"properties":{"total_30d":{"type":"integer","title":"Total 30D"},"you_archetype_share_30d":{"type":"number","title":"You Archetype Share 30D"},"you_percentile":{"$ref":"#/components/schemas/MeterPercentile"}},"type":"object","required":["total_30d","you_archetype_share_30d","you_percentile"],"title":"MeterAggregate"},"MeterAnswers":{"properties":{"k1":{"type":"number","title":"K1"},"k2":{"type":"number","title":"K2"},"k3":{"type":"number","title":"K3"},"s1":{"type":"number","title":"S1"},"s2":{"type":"number","title":"S2"},"s3":{"type":"number","title":"S3"},"a1":{"type":"number","title":"A1"},"a2":{"type":"number","title":"A2"},"a3":{"type":"number","title":"A3"}},"type":"object","required":["k1","k2","k3","s1","s2","s3","a1","a2","a3"],"title":"MeterAnswers","description":"The nine quiz responses. Each value must be one of the four canonical\nradio answers (``-1.2, -0.4, 0.4, 1.2``). The resolver clamps, but the\nAPI edge rejects anything outside the set so client-side bugs surface\nas 422 rather than silently-clamped takes."},"MeterArchetypeShare":{"properties":{"n":{"type":"integer","title":"N"},"share":{"type":"number","title":"Share"}},"type":"object","required":["n","share"],"title":"MeterArchetypeShare","description":"Per-archetype breakdown shape for the heatmap aggregate.\n\nMatches the v6 API contract §3.2: each archetype carries both a raw\ncount and a share (0..1) over the total opt-in takes in the window."},"MeterDeleteRequest":{"properties":{"confirm":{"type":"boolean","const":true,"title":"Confirm"},"anon_id":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9]{22}$"},{"type":"null"}],"title":"Anon Id"}},"type":"object","required":["confirm"],"title":"MeterDeleteRequest","description":"``DELETE /api/meter/takes/mine`` body. Requires explicit confirmation\nto avoid clearing history by accident."},"MeterHeatmapResponse":{"properties":{"since":{"type":"string","title":"Since"},"total":{"type":"integer","title":"Total"},"floor":{"type":"integer","title":"Floor"},"is_early_data":{"type":"boolean","title":"Is Early Data","default":false},"grid":{"items":{"items":{"type":"integer"},"type":"array"},"type":"array","title":"Grid"},"archetypes":{"additionalProperties":{"$ref":"#/components/schemas/MeterArchetypeShare"},"type":"object","title":"Archetypes"},"klik_band":{"additionalProperties":{"$ref":"#/components/schemas/MeterArchetypeShare"},"type":"object","title":"Klik Band"},"generated_at":{"type":"string","title":"Generated At"}},"type":"object","required":["since","total","floor","grid","archetypes","klik_band","generated_at"],"title":"MeterHeatmapResponse"},"MeterPercentile":{"properties":{"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"}},"type":"object","required":["x","y"],"title":"MeterPercentile"},"MeterResolutionPayload":{"properties":{"archetype":{"type":"string","enum":["guardian","pioneer","pragmatist","avoider","booster","pre_click"],"title":"Archetype"},"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"},"klik":{"type":"number","title":"Klik"},"confidence":{"type":"string","enum":["low","medium","high"],"title":"Confidence"},"primary_signal":{"type":"string","enum":["klik","fear","optimism","passive","leaning_in"],"title":"Primary Signal"},"supporting_signals":{"items":{"type":"string"},"type":"array","title":"Supporting Signals"},"stage_id":{"type":"string","enum":["dismisser","threatened","burned","curious","clicked","practitioner","on_the_edge"],"title":"Stage Id"},"action_depth":{"type":"string","enum":["none","casual","assisted_task","recurring_workflow","agentic_workflow"],"title":"Action Depth"},"agentic_flag":{"type":"boolean","title":"Agentic Flag"}},"type":"object","required":["archetype","x","y","klik","confidence","primary_signal","stage_id","action_depth","agentic_flag"],"title":"MeterResolutionPayload","description":"Client-submitted resolution. The server treats this as a hint —\nevery submit re-runs ``resolve_meter`` and overwrites if the client's\noutput differs (see plan-5 §5 Q3 — drift-guarded by the parity\nfixture)."},"MeterSubmitRequest":{"properties":{"anchor_type":{"type":"string","enum":["anon","user"],"title":"Anchor Type"},"anon_id":{"anyOf":[{"type":"string","pattern":"^[A-Za-z0-9]{22}$"},{"type":"null"}],"title":"Anon Id"},"answers":{"$ref":"#/components/schemas/MeterAnswers"},"resolution":{"$ref":"#/components/schemas/MeterResolutionPayload"},"locale":{"type":"string","enum":["en","sl"],"title":"Locale"}},"type":"object","required":["anchor_type","answers","resolution","locale"],"title":"MeterSubmitRequest","description":"Body schema for ``POST /api/meter/submit``."},"MeterSubmitResponse":{"properties":{"take_id":{"type":"string","title":"Take Id"},"share_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Share Token"},"aggregate":{"$ref":"#/components/schemas/MeterAggregate"},"resolution":{"$ref":"#/components/schemas/MeterResolutionPayload"}},"type":"object","required":["take_id","aggregate","resolution"],"title":"MeterSubmitResponse"},"MeterTakeOut":{"properties":{"take_id":{"type":"string","title":"Take Id"},"share_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Share Token"},"created_at":{"type":"string","title":"Created At"},"archetype":{"type":"string","title":"Archetype"},"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"},"klik":{"type":"number","title":"Klik"},"confidence":{"type":"string","title":"Confidence"},"primary_signal":{"type":"string","title":"Primary Signal"},"stage_id":{"type":"string","title":"Stage Id"},"action_depth":{"type":"string","title":"Action Depth"},"agentic_flag":{"type":"boolean","title":"Agentic Flag"},"locale":{"type":"string","title":"Locale"},"opt_in_share":{"type":"boolean","title":"Opt In Share"}},"type":"object","required":["take_id","created_at","archetype","x","y","klik","confidence","primary_signal","stage_id","action_depth","agentic_flag","locale","opt_in_share"],"title":"MeterTakeOut"},"MeterTakesMineResponse":{"properties":{"takes":{"items":{"$ref":"#/components/schemas/MeterTakeOut"},"type":"array","title":"Takes"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["takes","total"],"title":"MeterTakesMineResponse"},"ModerationPostQueueItem":{"properties":{"post":{"$ref":"#/components/schemas/PostOut"},"last_event_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Event Kind"},"last_event_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Event At"}},"type":"object","required":["post"],"title":"ModerationPostQueueItem"},"ModerationPostQueueOut":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ModerationPostQueueItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"ModerationPostQueueOut"},"ModerationQueueItem":{"properties":{"comment":{"$ref":"#/components/schemas/CommentOut"},"flag_count":{"type":"integer","title":"Flag Count"},"last_event_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Event Kind"},"last_event_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Event At"}},"type":"object","required":["comment","flag_count"],"title":"ModerationQueueItem"},"ModerationQueueOut":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ModerationQueueItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"ModerationQueueOut"},"NicknameUpdate":{"properties":{"nickname":{"type":"string","maxLength":60,"minLength":1,"title":"Nickname"}},"type":"object","required":["nickname"],"title":"NicknameUpdate","description":"Body for ``PATCH /api/account/nickname``."},"OAuthClientOut":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"registered_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Registered At"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"access_tokens_active":{"type":"integer","title":"Access Tokens Active"},"refresh_tokens_active":{"type":"integer","title":"Refresh Tokens Active"}},"type":"object","required":["client_id","client_name","registered_at","scopes","last_used_at","access_tokens_active","refresh_tokens_active"],"title":"OAuthClientOut"},"OptInBody":{"properties":{"opt_in":{"type":"boolean","title":"Opt In"},"anon_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Anon Id"}},"type":"object","required":["opt_in"],"title":"OptInBody"},"PostAuthor":{"properties":{"id":{"type":"string","title":"Id"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"type":"string","title":"Nickname"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"role":{"$ref":"#/components/schemas/UserRole"}},"type":"object","required":["id","nickname","role"],"title":"PostAuthor","description":"Author projection on post rows.\n\nUser-anonymity refactor: the canonical fields are ``handle`` +\n``nickname``. ``display_name`` is retained as a back-compat alias\npopulated with the same value as ``nickname`` for one deploy\ncycle so existing frontend renderers keep working."},"PostCreate":{"properties":{"title":{"type":"string","maxLength":200,"minLength":1,"title":"Title"},"summary":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Summary"},"body_md":{"type":"string","maxLength":30000,"minLength":1,"title":"Body Md"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"attachments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Attachments"},"vision":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Vision"},"quick":{"type":"boolean","title":"Quick","default":false}},"type":"object","required":["title","body_md"],"title":"PostCreate"},"PostList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PostListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"PostList"},"PostListItem":{"properties":{"id":{"type":"string","title":"Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"author":{"$ref":"#/components/schemas/PostAuthor"},"state":{"type":"string","title":"State"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"published_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published At"},"created_at":{"type":"string","title":"Created At"},"comment_count":{"type":"integer","title":"Comment Count","default":0},"summary_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Md"},"summary_candidate_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Md"},"summary_candidate_byot_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Byot Model"},"summary_candidate_generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Generated At"},"summary_byot_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Byot Model"}},"type":"object","required":["id","slug","title","author","state","created_at"],"title":"PostListItem"},"PostModeratorCreate":{"properties":{"user_id":{"type":"string","title":"User Id"}},"type":"object","required":["user_id"],"title":"PostModeratorCreate","description":"Body for ``POST /api/posts/{post_id}/moderators``."},"PostModeratorList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PostModeratorOut"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"PostModeratorList"},"PostModeratorOut":{"properties":{"user_id":{"type":"string","title":"User Id"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"type":"string","title":"Nickname"},"display_name":{"type":"string","title":"Display Name"},"added_at":{"type":"string","title":"Added At"},"added_by":{"type":"string","title":"Added By"}},"type":"object","required":["user_id","nickname","display_name","added_at","added_by"],"title":"PostModeratorOut","description":"One row from ``GET /api/posts/{post_id}/moderators``.\n\nOriginator is NOT included here (they're implicit moderators of\ntheir own topic and never appear in the ``post_moderator`` table)."},"PostOut":{"properties":{"id":{"type":"string","title":"Id"},"slug":{"type":"string","title":"Slug"},"title":{"type":"string","title":"Title"},"summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary"},"author":{"$ref":"#/components/schemas/PostAuthor"},"state":{"type":"string","title":"State"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"published_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published At"},"created_at":{"type":"string","title":"Created At"},"comment_count":{"type":"integer","title":"Comment Count","default":0},"summary_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Md"},"summary_candidate_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Md"},"summary_candidate_byot_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Byot Model"},"summary_candidate_generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Candidate Generated At"},"summary_byot_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Byot Model"},"body_md":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Md"},"body_html":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Body Html"},"attachments":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Attachments","default":[]},"triage_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Triage Reason"},"archive_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Archive Reason"},"updated_at":{"type":"string","title":"Updated At"},"vision":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vision"},"summary_generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Generated At"},"related_topic_slugs":{"items":{"type":"string"},"type":"array","title":"Related Topic Slugs","default":[]},"useful_count":{"type":"integer","title":"Useful Count","default":0},"has_useful":{"type":"boolean","title":"Has Useful","default":false},"vision_edit_count_30d":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vision Edit Count 30D"}},"type":"object","required":["id","slug","title","author","state","created_at","updated_at"],"title":"PostOut"},"PostStocks":{"properties":{"total_published":{"type":"integer","title":"Total Published"},"in_moderation":{"type":"integer","title":"In Moderation"}},"type":"object","required":["total_published","in_moderation"],"title":"PostStocks"},"PostUpdate":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Title"},"summary":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Summary"},"body_md":{"anyOf":[{"type":"string","maxLength":30000,"minLength":1},{"type":"null"}],"title":"Body Md"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"attachments":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Attachments"}},"type":"object","title":"PostUpdate"},"PostUsefulOut":{"properties":{"useful_count":{"type":"integer","title":"Useful Count"},"has_useful":{"type":"boolean","title":"Has Useful"}},"type":"object","required":["useful_count","has_useful"],"title":"PostUsefulOut","description":"Response from POST /api/posts/{post_id}/useful (toggle)."},"PostVisionUpdate":{"properties":{"vision":{"type":"string","maxLength":2000,"title":"Vision"}},"type":"object","required":["vision"],"title":"PostVisionUpdate","description":"Body for ``PATCH /api/posts/{id}/vision``.\n\nEmpty string is allowed — it clears the vision. Field-level\n``max_length`` mirrors the column constraint applied in the migration\n+ ORM (column is TEXT, but the API surface caps at 2000 chars)."},"ProposalFlagIn":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","title":"ProposalFlagIn"},"ProposalFlagOut":{"properties":{"flag_count":{"type":"integer","title":"Flag Count"},"auto_rejected":{"type":"boolean","title":"Auto Rejected","default":false},"status":{"type":"string","title":"Status"}},"type":"object","required":["flag_count","status"],"title":"ProposalFlagOut"},"ProposalList":{"properties":{"proposals":{"items":{"$ref":"#/components/schemas/ProposalOut"},"type":"array","title":"Proposals"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["proposals","total"],"title":"ProposalList"},"ProposalOut":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","title":"Kind"},"target_type":{"type":"string","title":"Target Type"},"target_id":{"type":"string","title":"Target Id"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"evidence":{"additionalProperties":true,"type":"object","title":"Evidence"},"provenance":{"type":"string","title":"Provenance"},"confidence":{"type":"string","title":"Confidence"},"status":{"type":"string","title":"Status"},"flag_count":{"type":"integer","title":"Flag Count","default":0},"created_at":{"type":"string","title":"Created At"},"decided_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decided At"},"decided_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decided By"},"applied_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Applied At"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["id","kind","target_type","target_id","payload","evidence","provenance","confidence","status","created_at"],"title":"ProposalOut"},"ProposalRejectIn":{"properties":{"reason_code":{"anyOf":[{"type":"string","enum":["invalid_slug","duplicate","out_of_taxonomy","low_signal","other"]},{"type":"null"}],"title":"Reason Code"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"ProposalRejectIn"},"PublicProposalList":{"properties":{"proposals":{"items":{"$ref":"#/components/schemas/PublicProposalSummary"},"type":"array","title":"Proposals"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["proposals","total"],"title":"PublicProposalList"},"PublicProposalSummary":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","title":"Kind"},"target_type":{"type":"string","title":"Target Type"},"target_id":{"type":"string","title":"Target Id"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"provenance":{"type":"string","title":"Provenance"},"confidence":{"type":"string","title":"Confidence"},"status":{"type":"string","title":"Status"},"co_mentions":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Co Mentions"},"flag_count":{"type":"integer","title":"Flag Count","default":0},"created_at":{"type":"string","title":"Created At"},"decided_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decided At"},"applied_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Applied At"}},"type":"object","required":["id","kind","target_type","target_id","payload","provenance","confidence","status","created_at"],"title":"PublicProposalSummary"},"PublishBody":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"override":{"type":"boolean","title":"Override","default":false},"moderator_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Moderator Notes"}},"type":"object","title":"PublishBody","description":"Optional body for ``POST /{video_id}/publish``.\n\n``override`` is the moderator's explicit opt-in to publish a fill\nwhose slots fail the catalogue validator (e.g. empty dict, missing\nrequired slots). Without ``override=true`` the route 422s and the\nfill stays in ``drafted``. ``reason`` (optional) is recorded both\non the moderation event and in the audit payload."},"ReactionCreate":{"properties":{"kind":{"type":"string","maxLength":16,"minLength":1,"title":"Kind"}},"type":"object","required":["kind"],"title":"ReactionCreate"},"ReactionOut":{"properties":{"comment_id":{"type":"string","title":"Comment Id"},"reaction_counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reaction Counts"},"my_reactions":{"items":{"type":"string"},"type":"array","title":"My Reactions"}},"type":"object","required":["comment_id","reaction_counts","my_reactions"],"title":"ReactionOut"},"RegenerateBody":{"properties":{"template_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Template Id"},"force":{"type":"boolean","title":"Force","default":true}},"type":"object","title":"RegenerateBody"},"RejectBody":{"properties":{"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","title":"RejectBody"},"RoleUpdate":{"properties":{"role":{"$ref":"#/components/schemas/UserRole"}},"type":"object","required":["role"],"title":"RoleUpdate"},"SchedulesResponse":{"properties":{"jobs":{"items":{"$ref":"#/components/schemas/JobOut"},"type":"array","title":"Jobs"}},"type":"object","required":["jobs"],"title":"SchedulesResponse"},"SearchResponse":{"properties":{"query":{"type":"string","title":"Query"},"results":{"items":{"$ref":"#/components/schemas/SearchResult"},"type":"array","title":"Results"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["query","results","total"],"title":"SearchResponse"},"SearchResult":{"properties":{"type":{"type":"string","title":"Type"},"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"snippet":{"type":"string","title":"Snippet"},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","default":[]},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"score":{"type":"number","title":"Score"}},"type":"object","required":["type","id","title","snippet","score"],"title":"SearchResult"},"SourceItem":{"properties":{"type":{"type":"string","title":"Type"},"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"snippet":{"type":"string","title":"Snippet"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Date"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"url":{"type":"string","title":"Url"}},"type":"object","required":["type","id","title","snippet","url"],"title":"SourceItem"},"SourceListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/SourceItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"SourceListResponse"},"SourceStocks":{"properties":{"total":{"type":"integer","title":"Total"}},"type":"object","required":["total"],"title":"SourceStocks"},"StocksResponse":{"properties":{"videos":{"$ref":"#/components/schemas/VideoStocks"},"knowledge_concepts":{"$ref":"#/components/schemas/KnowledgeStocks"},"essays":{"$ref":"#/components/schemas/CountOnly"},"digests":{"$ref":"#/components/schemas/DigestStocks"},"personas":{"$ref":"#/components/schemas/CountOnly"},"posts":{"$ref":"#/components/schemas/PostStocks"},"sources":{"$ref":"#/components/schemas/SourceStocks"},"embeddings":{"$ref":"#/components/schemas/EmbeddingStocks"}},"type":"object","required":["videos","knowledge_concepts","essays","digests","personas","posts","sources","embeddings"],"title":"StocksResponse"},"SubsystemsResponse":{"properties":{"checks":{"items":{"$ref":"#/components/schemas/HealthCheck"},"type":"array","title":"Checks"},"overall":{"type":"string","title":"Overall"}},"type":"object","required":["checks","overall"],"title":"SubsystemsResponse","description":"Subsystem health pings — anonymous-readable."},"SummaryFlagRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"},"summary_generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Generated At"}},"type":"object","title":"SummaryFlagRequest","description":"Body for ``POST /api/posts/{id}/summary/flag``.\n\nBoth fields are optional — a zero-body POST is valid (we record \"no\nreason given\"). ``summary_generated_at`` helps moderators correlate the\nflag to the specific summary version the reader saw."},"SystemHealthResponse":{"properties":{"status":{"type":"string","title":"Status"},"dev_mode":{"type":"boolean","title":"Dev Mode"},"project_root":{"type":"string","title":"Project Root"},"channels_json_exists":{"type":"boolean","title":"Channels Json Exists"},"channels_dir_exists":{"type":"boolean","title":"Channels Dir Exists"},"essays_dir_exists":{"type":"boolean","title":"Essays Dir Exists"},"ai_provider":{"type":"string","title":"Ai Provider"},"skill_url":{"type":"string","title":"Skill Url"}},"type":"object","required":["status","dev_mode","project_root","channels_json_exists","channels_dir_exists","essays_dir_exists","ai_provider","skill_url"],"title":"SystemHealthResponse","description":"Detailed system health — admin-only.\n\nThe legacy shape that used to live at the public ``/api/health`` endpoint.\nHolds operational details (paths, providers, wiring) that shouldn't\nleak anonymously."},"TemplateFillPut":{"properties":{"template_id":{"type":"string","title":"Template Id"},"slots":{"additionalProperties":true,"type":"object","title":"Slots"},"moderator_notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Moderator Notes"}},"type":"object","required":["template_id","slots"],"title":"TemplateFillPut"},"TokenRequest":{"properties":{"grant_type":{"type":"string","title":"Grant Type"},"device_code":{"type":"string","title":"Device Code"}},"type":"object","required":["grant_type","device_code"],"title":"TokenRequest"},"TopicSummary":{"properties":{"id":{"type":"string","title":"Id"},"slug":{"type":"string","title":"Slug"},"archive_id":{"type":"string","title":"Archive Id"},"title":{"type":"string","title":"Title"},"vision":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Vision"},"useful_count":{"type":"integer","title":"Useful Count","default":0},"comment_count":{"type":"integer","title":"Comment Count","default":0},"vision_edit_count_30d":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Vision Edit Count 30D"},"summary_generated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Summary Generated At"},"author_display_name":{"type":"string","title":"Author Display Name","default":""}},"type":"object","required":["id","slug","archive_id","title"],"title":"TopicSummary","description":"Slim projection of a published post suitable for the homepage\nActive Discussions band."},"TopicSummaryList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/TopicSummary"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","limit","offset"],"title":"TopicSummaryList"},"UnreadCountOut":{"properties":{"count":{"type":"integer","title":"Count"}},"type":"object","required":["count"],"title":"UnreadCountOut"},"UnreadOut":{"properties":{"count":{"type":"integer","title":"Count"}},"type":"object","required":["count"],"title":"UnreadOut"},"UserMeOut":{"properties":{"id":{"type":"string","title":"Id"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nickname"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"role":{"$ref":"#/components/schemas/UserRole"},"created_at":{"type":"string","title":"Created At"},"reputation":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Reputation"},"email":{"type":"string","title":"Email"},"setup_required":{"type":"boolean","title":"Setup Required","default":false},"handle_changed_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle Changed At"}},"type":"object","required":["id","role","created_at","email"],"title":"UserMeOut","description":"Self-view projection — extends :class:`UserOut` with email + setup signal.\n\n``email`` is intentionally only on the self-view; the public\n``/api/users/{handle}`` route does NOT include it. ``setup_required``\nis ``True`` for a fresh OAuth user whose ``handle`` is still NULL —\nthe frontend bounces them to ``/account/setup`` until they pick one."},"UserPublicOut":{"properties":{"id":{"type":"string","title":"Id"},"handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Handle"},"nickname":{"type":"string","title":"Nickname"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"avatar_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Avatar Url"},"bio":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bio"},"role":{"$ref":"#/components/schemas/UserRole"},"reputation":{"additionalProperties":true,"type":"object","title":"Reputation"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","nickname","role","reputation","created_at"],"title":"UserPublicOut","description":"Public user profile — surfaces handle + nickname, never email."},"UserRole":{"type":"string","enum":["user","moderator","admin"],"title":"UserRole"},"UserUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":120},{"type":"null"}],"title":"Display Name"},"bio":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Bio"}},"type":"object","title":"UserUpdate"},"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"},"VideoStocks":{"properties":{"total":{"type":"integer","title":"Total"},"relevant":{"type":"integer","title":"Relevant"},"knowledge":{"type":"integer","title":"Knowledge"},"highlight":{"type":"integer","title":"Highlight"}},"type":"object","required":["total","relevant","knowledge","highlight"],"title":"VideoStocks"},"VoteOut":{"properties":{"count":{"type":"integer","title":"Count"},"distinct_voters_with_rep_above_zero":{"type":"integer","title":"Distinct Voters With Rep Above Zero"},"you_voted":{"type":"boolean","title":"You Voted"}},"type":"object","required":["count","distinct_voters_with_rep_above_zero","you_voted"],"title":"VoteOut"},"_DCRRequest":{"properties":{"client_name":{"type":"string","title":"Client Name","default":"unnamed"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris","default":[]},"token_endpoint_auth_method":{"type":"string","title":"Token Endpoint Auth Method","default":"none"},"logo_uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Logo Uri"},"client_uri":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Uri"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"}},"type":"object","title":"_DCRRequest"},"_UserBrief":{"properties":{"id":{"type":"string","title":"Id"},"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["id","display_name"],"title":"_UserBrief"},"app__routers__comment_schemas__ModerationActionRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reason"}},"type":"object","title":"ModerationActionRequest"},"app__routers__me_notifications__NotificationList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/app__routers__me_notifications__NotificationOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"unread_count":{"type":"integer","title":"Unread Count"}},"type":"object","required":["items","total","unread_count"],"title":"NotificationList"},"app__routers__me_notifications__NotificationOut":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","title":"Kind"},"entity_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Type"},"entity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Entity Id"},"body":{"type":"string","title":"Body"},"actor_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor User Id"},"actor_display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Display Name"},"payload":{"additionalProperties":true,"type":"object","title":"Payload","default":{}},"read_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","kind","body","created_at"],"title":"NotificationOut"},"app__routers__notification_schemas__NotificationList":{"properties":{"items":{"items":{"$ref":"#/components/schemas/app__routers__notification_schemas__NotificationOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"},"unread":{"type":"integer","title":"Unread"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["items","total","unread","limit","offset"],"title":"NotificationList"},"app__routers__notification_schemas__NotificationOut":{"properties":{"id":{"type":"string","title":"Id"},"kind":{"type":"string","title":"Kind"},"body":{"type":"string","title":"Body"},"archive_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Archive Id"},"actor_handle":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Handle"},"target_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Url"},"data":{"additionalProperties":true,"type":"object","title":"Data","default":{}},"read_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Read At"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","kind","body","created_at"],"title":"NotificationOut"},"app__routers__post_schemas__ModerationActionRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","title":"ModerationActionRequest"}},"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"Agent API key. Pass as `Authorization: Bearer sok_...`. Obtain via the RFC 8628 device flow at /api/oauth/device + /api/oauth/token."},"CookieAuth":{"type":"apiKey","in":"cookie","name":"soy_session","description":"Browser session cookie set by /api/auth/google/callback. Write endpoints additionally require the CSRFHeader."},"CSRFHeader":{"type":"apiKey","in":"header","name":"X-CSRF-Token","description":"Required on write requests authenticated by CookieAuth. Mirrored from the soy_csrf cookie."}}}}