{"openapi":"3.1.0","info":{"title":"Kagura Memory Cloud","description":"Universal AI Memory Platform - Remote MCP Server + Web Management","version":"0.117.0"},"paths":{"/api/v1/auth/providers":{"get":{"tags":["authentication"],"summary":"Get Auth Providers","description":"Return list of enabled OAuth providers for login page.\n\nChecks AUTH_PROVIDERS setting and configured credentials.\nCached per process (env vars don't change at runtime).","operationId":"get_auth_providers_api_v1_auth_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvidersResponse"}}}}}}},"/api/v1/auth/logout":{"post":{"tags":["authentication"],"summary":"Logout","description":"Sign out — either the active account, or every account (#1488 Phase 4).\n\nIssue #93-2: Implement proper logout\nIssue #115: Cookie name changed from 'session_id' to 'kagura_session'\n\nOnce one session can hold several accounts, \"log out\" stops having a single\nmeaning, and the two meanings are not interchangeable: ending the session\nwhen the user meant \"sign out of this one\" evicts accounts they never\ntouched, and the reverse leaves them signed in when they asked not to be.\n`scope` names which one is intended.\n\n**The default is ``all``**, so every existing caller — and any older\nfrontend still deployed during a rollout — keeps the exact behaviour it had\nbefore this route learned about accounts.\n\nCarried as a query parameter rather than a body: `apiClient.post()` sends\n`Content-Type: application/json` with no body when called without one, and\na body-typed parameter makes that pairing a parsing edge case for no gain.\n`/auth/login?return_to=` and `/auth/google/login?add_account=1` in this same\nrouter already read state-changing input this way.\n\nDeliberately NOT behind an auth dependency, as before. Logging out with an\nexpired session must succeed rather than 401 — the user's intent is already\nsatisfied, and a client that cannot log out of a dead session is stuck.\n\nReturns ``session_ended`` because the caller's next move depends on it:\nFalse means another account is now active and the page should be reloaded\nas them; True means go to /login. Two paths reach True with\n``scope=\"current\"`` — the active account was the last one, and the removal\ncould not be completed. The second is a deliberate fail-safe: refusing to\nremove one account is not a reason to leave the user signed in as it, so it\ndegrades to a full sign-out, which always satisfies the request.\n\nExample:\n    POST /auth/logout\n    Cookie: kagura_session=...\n    Response: {\"success\": true, \"scope\": \"all\", \"session_ended\": true}\n    Set-Cookie: kagura_session=; Max-Age=0","operationId":"logout_api_v1_auth_logout_post","parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["current","all"],"type":"string","description":"'all' ends the whole browser session (every signed-in account). 'current' signs out only the active account, leaving the others.","default":"all","title":"Scope"},"description":"'all' ends the whole browser session (every signed-in account). 'current' signs out only the active account, leaving the others."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/accounts":{"get":{"tags":["authentication"],"summary":"List Signed In Accounts","description":"List the accounts signed in on THIS browser session (#1488 Phase 2).\n\nReads only from the caller's own session container, so it can never\nenumerate anyone else's identities. Returns the active one flagged, which\nis what the sidebar switcher renders.","operationId":"list_signed_in_accounts_api_v1_auth_accounts_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/accounts/switch":{"post":{"tags":["authentication"],"summary":"Switch Active Account","description":"Make another already-signed-in account active (#1488 Phase 2).\n\nThe security boundary is `switch_account`'s membership check: the target\nmust already be in THIS session's container. A caller naming an arbitrary\nuser id gets 404, not that user's session — there is no lookup anywhere in\nthis path, so no id a caller invents can widen their access.\n\n404 rather than 403 on a non-member: whether some other account exists is\nnot this caller's business to learn.","operationId":"switch_active_account_api_v1_auth_accounts_switch_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AccountSwitchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/me":{"get":{"tags":["authentication"],"summary":"Get Current User Info","description":"Get current authenticated user info.\n\nIssue #252: Now uses require_session_auth (Session-only, no API keys).\n\nArgs:\n    user: Current user from session (via require_session_auth)\n    db: Database session\n\nReturns:\n    User information from session + database (wrapped in \"user\" object for frontend compatibility)\n\nRaises:\n    HTTPException:\n        - 401 if not authenticated\n        - 403 if API key is provided\n\nExample:\n    GET /api/v1/auth/me\n    Cookie: kagura_session=...\n    Response: {\n        \"user\": {\n            \"id\": \"google_123\",\n            \"email\": \"user@example.com\",\n            \"name\": \"Example User\",\n            \"role\": \"admin\",\n            \"current_workspace_id\": \"uuid...\"\n        }\n    }\n\nNote:\n    Frontend expects {user: {...}} format (Issue #664).\n    Response wrapped in \"user\" object for compatibility.","operationId":"get_current_user_info_api_v1_auth_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/config":{"get":{"tags":["authentication"],"summary":"Get Auth Config","description":"Get authentication configuration (public).","operationId":"get_auth_config_api_v1_auth_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/auth/login":{"post":{"tags":["authentication"],"summary":"Password Login","description":"Authenticate with a login ID or a verified email, and a password.\n\n``login_id`` carries either an admin login ID or (#1678) the verified\nemail of an account that has a password — see\n``resolve_password_login_user``. Every failure is the same generic 401;\nan unknown identifier costs the same bcrypt work as a wrong password.\nFailures count per resolved account, or per normalized identifier when\nnothing resolves.","operationId":"password_login_api_v1_auth_login_post","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordLoginRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/mfa/verify":{"post":{"tags":["authentication"],"summary":"Mfa Verify","description":"Verify TOTP code and create session.","operationId":"mfa_verify_api_v1_auth_mfa_verify_post","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MfaVerifyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/google/login":{"get":{"tags":["authentication","authentication","google-oauth2"],"summary":"Google Login","description":"Initiate Google OAuth2 login flow.\n\nGenerates OAuth2 authorization URL with CSRF state token.\n\nArgs:\n    redirect_uri: Optional custom redirect URI (defaults to configured URI)\n    return_to: If provided, auto-redirects browser to Google (for iOS/browser users)\n    invite: Optional closed-beta invite token (#1581). Its hash is bound to\n        this flow's state for the signup gate; malformed values are ignored.\n    accepted_terms: The terms version the person agreed to on the sign-in\n        page (#1665). Bound to this flow's state; ignored while\n        ``TERMS_VERSION`` is empty.\n    link_proof: This sign-in proves the account for an identity link\n        (#1818): Google is asked for ``auth_time``, and the callback\n        records it from the verified ID token. Ordinary sign-ins send the\n        same request as before.\n\nReturns:\n    If return_to: RedirectResponse to Google OAuth (browser user)\n    Otherwise: JSON {authorization_url, state} (API client)\n\nExample:\n    GET /api/v1/auth/google/login (API client)\n    Response: {\"authorization_url\": \"...\", \"state\": \"...\"}\n\n    GET /api/v1/auth/google/login?return_to=... (browser user)\n    Response: 303 Redirect to Google OAuth\n\nNote:\n    Frontend should redirect user to authorization_url.\n    State token is stored in Redis for CSRF validation.","operationId":"google_login_api_v1_auth_google_login_get","parameters":[{"name":"redirect_uri","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Redirect Uri"}},{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}},{"name":"add_account","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Add Account"}},{"name":"invite","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite"}},{"name":"accepted_terms","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accepted Terms"}},{"name":"link_proof","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Link Proof"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/google/callback":{"get":{"tags":["authentication","authentication","google-oauth2"],"summary":"Google Callback","description":"Handle Google OAuth2 callback.\n\nExchanges authorization code for access token, retrieves user info,\ncreates session, and sets HttpOnly cookie.\n\nArgs:\n    request: FastAPI request (used for IP / User-Agent capture on the\n        audit row written by ``RoleManager.ensure_user`` when the IdP-\n        provided email differs from the stored value).\n    code: OAuth2 authorization code from Google.\n    state: CSRF state token (must match stored state).\n\nReturns:\n    Redirect to dashboard with session cookie set. Every failure class on\n    this browser-facing route 303-redirects to ``/login`` with a\n    well-known ``error`` token (#1381) — raw JSON is never the terminal\n    state for an interactive user.\n\nExample:\n    GET /api/v1/auth/google/callback?code=xxx&state=yyy\n    → Sets cookie: kagura_session=...\n    → Redirects to /dashboard\n\nSecurity:\n    - State validation (CSRF protection)\n    - HttpOnly cookie (XSS protection)\n    - Secure cookie (HTTPS only)\n    - SameSite=Lax (CSRF protection)","operationId":"google_callback_api_v1_auth_google_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth2 authorization code","title":"Code"},"description":"OAuth2 authorization code"},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSRF state token","title":"State"},"description":"CSRF state token"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth2 error code (e.g. access_denied) when the user cancels","title":"Error"},"description":"OAuth2 error code (e.g. access_denied) when the user cancels"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable OAuth2 error detail (not reflected)","title":"Error Description"},"description":"Human-readable OAuth2 error detail (not reflected)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/github/login":{"get":{"tags":["authentication","authentication","github-oauth2"],"summary":"Github Login","description":"Initiate GitHub OAuth2 login flow.\n\n``invite`` is an optional closed-beta invite token (#1581) and\n``accepted_terms`` the terms version agreed to (#1665) — see\n``google_login``; both providers share ``_remember_beta_invite`` and\n``_remember_accepted_terms``.","operationId":"github_login_api_v1_auth_github_login_get","parameters":[{"name":"return_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}},{"name":"add_account","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Add Account"}},{"name":"invite","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Invite"}},{"name":"accepted_terms","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Accepted Terms"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/github/callback":{"get":{"tags":["authentication","authentication","github-oauth2"],"summary":"Github Callback","description":"Handle GitHub OAuth2 callback.\n\nSame flow as Google callback: CSRF validation → token exchange →\nuser info → session creation → cookie → redirect.","operationId":"github_callback_api_v1_auth_github_callback_get","parameters":[{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"GitHub authorization code","title":"Code"},"description":"GitHub authorization code"},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSRF state token","title":"State"},"description":"CSRF state token"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth2 error code (e.g. access_denied) when the user cancels","title":"Error"},"description":"OAuth2 error code (e.g. access_denied) when the user cancels"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable OAuth2 error detail (not reflected)","title":"Error Description"},"description":"Human-readable OAuth2 error detail (not reflected)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me":{"get":{"tags":["users"],"summary":"Get Current User","description":"Get current user profile.\n\nIssue #252: Session-only (no API keys).\n\nReturns:\n    Current user information including timezone\n\nExample:\n    GET /api/v1/users/me\n    Response: {\"email\": \"...\", \"timezone\": \"Asia/Tokyo\", ...}","operationId":"get_current_user_api_v1_users_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/profile":{"get":{"tags":["users"],"summary":"Get User Profile","description":"Get current user profile.\n\nIssue #221: User profile with locale support\nIssue #252: Session-only (no API keys)\n\nReturns:\n    User profile including locale, timezone, etc.\n\nExample:\n    GET /api/v1/users/profile\n    Response: {\"email\": \"...\", \"locale\": \"ja\", \"timezone\": \"Asia/Tokyo\", ...}","operationId":"get_user_profile_api_v1_users_profile_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["users"],"summary":"Update User Profile","description":"Update user profile (name, timezone, locale).\n\nIssue #175: User timezone settings\nIssue #221: i18n support (locale)\nIssue #252: Session-only (no API keys)\n\nArgs:\n    data: Profile update data (name, timezone, locale)\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Updated user profile\n\nExample:\n    PUT /api/v1/users/profile\n    Body: {\"locale\": \"ja\", \"timezone\": \"Asia/Tokyo\"}\n    Response: {\"email\": \"...\", \"locale\": \"ja\", \"timezone\": \"Asia/Tokyo\", ...}","operationId":"update_user_profile_api_v1_users_profile_put","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserProfileRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserProfileResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/erasure-request":{"post":{"tags":["account-erasure"],"summary":"Create Erasure Request","description":"Create a pending erasure request and issue a one-time token.\n\nReturns 201 with the new request's state. The ``confirm_token`` field\nin the response is populated for password-auth users and ``null`` for\nOAuth users (Issue #469): OAuth users receive the token via email\ninstead, keeping the raw secret out of the response surface.\n\nOther status codes:\n    - 403: user is the protected initial admin\n    - 409: an active erasure request already exists for this user\n      (``ERASURE-006``), or the user owns a workspace with a running\n      paid subscription (``BILLING-005``, Issue #1940)\n    - 503: OAuth user but the confirmation email failed to dispatch\n      (mapped from EmailDispatchError); the pending row is rolled back\n      so the user can retry once the email backend recovers.\n\nThe receipt notification (separate from the OAuth confirmation email)\nis dispatched post-commit and is fire-and-forget — it tells the user\nthe request was received and is meant to be obvious to the human even\nif they don't click any confirm link.","operationId":"create_erasure_request_api_v1_me_account_erasure_request_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErasureRequestCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["account-erasure"],"summary":"Cancel Erasure Request","description":"Cancel a cooling_off request before it executes.\n\nReturns 404 if there is no active request to cancel.","operationId":"cancel_erasure_request_api_v1_me_account_erasure_request_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErasureRequestStateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["account-erasure"],"summary":"Get Active Erasure Request","description":"Return the user's active erasure request, or null if none.\n\n\"Active\" = pending OR cooling_off OR in_progress. Terminal states\n(complete / failed / cancelled) are not surfaced here — that's an\nadmin-side audit concern, not user-facing UX.","operationId":"get_active_erasure_request_api_v1_me_account_erasure_request_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ErasureRequestStateResponse"},{"type":"null"}],"title":"Response Get Active Erasure Request Api V1 Me Account Erasure Request Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/erasure-confirm":{"post":{"tags":["account-erasure"],"summary":"Confirm Erasure Request","description":"Confirm a pending request and start the 7-day cooling-off period.\n\nPassword-auth users must re-supply their password as a second factor.\nOAuth users rely on the email-link click + active session cookie.\nReturns 400 on invalid/expired token, 403 on password mismatch, 409\n``BILLING-005`` when the user owns a workspace with a running paid\nsubscription (Issue #1940).","operationId":"confirm_erasure_request_api_v1_me_account_erasure_confirm_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErasureConfirmRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErasureRequestStateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/link-provider":{"post":{"tags":["account-erasure","account-linking"],"summary":"Link Provider","description":"Attach a sign-in provider to the current account: start a link-mode\nOAuth round-trip. This adds a way to sign in to ONE account; counting two\naccounts as one owner is an identity link (``/identity-links``).\n\nThe OAuth round-trip *is* the fresh re-auth — there is no password\nprompt, which is the locked re-auth contract for OAuth-only users\n(edge case 1). The existing ``/auth/{provider}/callback`` reads\n``oauth2_state_intent:{state}`` == ``\"link\"`` and binds the returned\nidentity to ``oauth2_state_user:{state}`` via ``AccountLinkingService`` —\nonly when the callback arrives with the session cookie pinned under\n``oauth2_state_session:{state}`` here, so the ``authorization_url`` is of\nno use to any other browser.\n\nReturns:\n    JSON with ``authorization_url`` (frontend redirects to it) and the\n    CSRF ``state`` token.\n\nRaises:\n    HTTPException(401): no session cookie on the request (``SessionUser``\n        is browser-session only, so this is a contradiction).\n    HTTPException(500): auth managers not initialised, OAuth2 manager\n        missing, or a required env var (GOOGLE_REDIRECT_URI /\n        GITHUB_CLIENT_ID) is missing.","operationId":"link_provider_api_v1_me_account_link_provider_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkProviderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LinkProviderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/unlink-provider":{"post":{"tags":["account-erasure","account-linking"],"summary":"Unlink Provider","description":"Detach a sign-in provider (a linked OAuth identity) from the current account.\n\nReturns ``{\"status\": \"ok\"}`` on success. The service guards the\ninvariants and raises, which the global ``memory_cloud_exception_handler``\nmaps to HTTP:\n\n    - 404 (NotFoundException): the provider is not linked to this account.\n    - 409 (ConflictError): removing it would leave zero sign-in methods.\n\nOn success the owner is emailed a security notice (Issue #1752).","operationId":"unlink_provider_api_v1_me_account_unlink_provider_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlinkProviderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlinkProviderResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/providers":{"get":{"tags":["account-erasure","account-linking"],"summary":"List Providers","description":"List the sign-in providers (OAuth identities) attached to the session user.","operationId":"list_providers_api_v1_me_account_providers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvidersListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/identity-links":{"get":{"tags":["account-erasure","identity-links"],"summary":"List Identity Links","description":"List the identity links of the session user, and the linkable accounts.\n\nIdentity-linked accounts (separate accounts, not sign-in providers) own\nthe same private contexts and the memories in them (roles and workspace\nmembership stay per account). Linkable accounts are the other accounts\nsigned in on this browser session.","operationId":"list_identity_links_api_v1_me_account_identity_links_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinksResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["account-erasure","identity-links"],"summary":"Link Identity","description":"Create an identity link: count another account as the same owner as\nthe session user. (A sign-in provider is attached with ``link-provider``.)\n\nThe proof is this browser session: the target must be one of the accounts\nsigned in on it, which each entered through its own sign-in, and both the\nsession user and the target must have proved their credential here within\n``IDENTITY_LINK_SIGN_IN_WINDOW`` (#1803, #1818) — a link is symmetric, so a stale\nsign-in on either side would let whoever holds an old session hand that\naccount's private contexts to another. Nothing is ever linked by an email\nmatch (#481). An id that is not in the session answers 404 whether or not\nsuch an account exists; freshness is checked only after that.\n\nEither account may already be in a link set; the sets then merge, and\nevery account of both is audited and sent the ``ACCOUNT_LINKED`` notice\n(#1875), not only the two named here.\n\nRaises (via the global handler): 422 for the caller's own id, 404 for an\naccount not signed in here, 403 (``AUTH-305``) when either account has not\nsigned in within the window, 409 when the set would exceed its size cap.","operationId":"link_identity_api_v1_me_account_identity_links_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinkTarget"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinkStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/identity-links/unlink":{"post":{"tags":["account-erasure","identity-links"],"summary":"Unlink Identity","description":"Remove an identity link: stop counting another account as the same\nowner as the session user.\n\nEither side can cut the link from its own session; the other account\ndoes not have to be signed in. 404 when the account is not linked.","operationId":"unlink_identity_api_v1_me_account_identity_links_unlink_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinkTarget"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinkStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/account/identity-links/leave":{"post":{"tags":["account-erasure","identity-links"],"summary":"Leave Identity Links","description":"Leave the identity-link set: take the session user out of it; the other\naccounts stay linked to each other (#1807).\n\n``unlink`` names one account to take out of the caller's set; ``leave``\ntakes the caller out. Every account of the former set is notified.\n404 when the session user is not linked to any account.","operationId":"leave_identity_links_api_v1_me_account_identity_links_leave_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityLinkStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/refresh-oauth":{"post":{"tags":["me-oauth"],"summary":"Refresh Oauth","description":"Initiate a manual IdP refresh for the current user.\n\nReturns:\n    JSON with ``authorization_url`` (frontend redirects to it) and\n    the CSRF ``state`` token (for symmetry with ``/auth/{provider}/login``).\n\nRaises:\n    HTTPException(400): user is password-auth, or has no usable\n        ``auth_provider`` (a legacy null from before #361, or the last\n        provider was unlinked). The pointer is set again only by a\n        sign-in through the identity the account was created with, once\n        its link is established (#1875, ``auth.roles``) — never by\n        linking a provider, nor by signing in through one attached\n        later, however old its link.\n    HTTPException(429): more than 1 request in the current minute\n        window. ``Retry-After`` header is set on the response.\n    HTTPException(500): OAuth managers not initialised, or required\n        environment variables missing.","operationId":"refresh_oauth_api_v1_me_refresh_oauth_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOAuthRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefreshOAuthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/oauth/grants":{"get":{"tags":["me-oauth"],"summary":"List Oauth Grants","description":"List the OAuth apps the caller has authorized, newest issuance first.","operationId":"list_oauth_grants_api_v1_me_oauth_grants_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OAuthGrantResponse"},"title":"Response List Oauth Grants Api V1 Me Oauth Grants Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/oauth/grants/{client_id}/default-context":{"put":{"tags":["me-oauth"],"summary":"Set Oauth Grant Default Context","description":"Set (or, with ``null``, clear) the MCP default context of one grant.\n\nThe context must be readable by the caller now (viewer); otherwise 404\n``Context not found``.\n\nRaises:\n    NotFoundException: The caller has no grant for ``client_id``, or\n        cannot read the context (404).","operationId":"set_oauth_grant_default_context_api_v1_me_oauth_grants__client_id__default_context_put","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthGrantDefaultContextUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["me-oauth"],"summary":"Clear Oauth Grant Default Context","description":"Clear the MCP default context of one grant.\n\nRaises:\n    NotFoundException: The caller has no grant for ``client_id`` (404).","operationId":"clear_oauth_grant_default_context_api_v1_me_oauth_grants__client_id__default_context_delete","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/terms-acceptance":{"post":{"tags":["me-terms"],"summary":"Accept Terms","description":"Record that the signed-in user accepts the current terms version.\n\nRaises:\n    HTTPException(404): ``TERMS_VERSION`` is not set.\n    HTTPException(409): ``version`` is not the current terms version.","operationId":"accept_terms_api_v1_me_terms_acceptance_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermsAcceptanceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TermsAcceptanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/password/reset-request":{"post":{"tags":["authentication"],"summary":"Request Password Reset","description":"Email a password-reset link if the address names an eligible account.\n\nAlways answers 202 with the same body. The request path only checks the\nrate limits and schedules the same background task for every address;\nthe account lookup, the token, the audit row and the email all happen\nafter the response, on their own session, so neither the answer nor its\ntiming reveals whether an account exists. Limited per client address\n(429) and per address (silently: no further emails).","operationId":"request_password_reset_api_v1_auth_password_reset_request_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordResetRequestBody"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordEmailAcceptedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/password/reset":{"post":{"tags":["authentication"],"summary":"Reset Password","description":"Set a new password from a reset link; sign out every browser session.\n\nReturns 204; the person then signs in with the new password. 400 for an\nunknown, expired or used link; 422 when the password breaks the policy.\nThe owner is emailed a security notice once the reset has committed.","operationId":"reset_password_api_v1_auth_password_reset_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordLinkBody"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/auth/password/setup":{"post":{"tags":["authentication"],"summary":"Setup Password","description":"Set the first password from a set-up link.\n\nFollowing the link proves the mailbox, so the email is marked verified.\nThe account's other browser sessions are revoked; a session in this browser (the\none that asked for the link) is kept. The owner is emailed a security notice.","operationId":"setup_password_api_v1_auth_password_setup_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordLinkBody"}}},"required":true},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/password/setup-request":{"post":{"tags":["account-password"],"summary":"Request Password Setup","description":"Email a set-a-password link to the signed-in account's address.\n\n409 when the account already has a password; 400 for a local CLI account;\n429 after a few requests; 503 when the email could not be sent.","operationId":"request_password_setup_api_v1_me_password_setup_request_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordSetupRequestedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/password/change":{"post":{"tags":["account-password"],"summary":"Change Password","description":"Change the password; every other browser session is revoked.\n\n403 when ``current_password`` is wrong; 409 when there is no password;\n422 when the new password breaks the policy. The owner is emailed a\nsecurity notice.","operationId":"change_password_api_v1_me_password_change_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordChangeBody"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/me/password":{"delete":{"tags":["account-password"],"summary":"Remove Password","description":"Remove the password. Refused (409) while no OAuth provider is linked.\n\n403 when ``current_password`` is wrong. Every other browser session is\nrevoked. The owner is emailed a security notice (sign-in method removed).","operationId":"remove_password_api_v1_me_password_delete","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PasswordRemoveBody"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/clients":{"get":{"tags":["oauth2-server"],"summary":"List Oauth2 Clients","description":"List OAuth2 clients owned by authenticated user's current context.\n\nIssue #82: Now context-scoped - returns clients for current context only.\n\nSECURITY FIX (Issue #93-3): Filter by owner_id\n\nReturns:\n    List of user's OAuth2 clients (without secrets)\n\nExample:\n    GET /api/v1/oauth/clients","operationId":"list_oauth2_clients_api_v1_oauth_clients_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/OAuth2ClientResponse"},"title":"Response List Oauth2 Clients Api V1 Oauth Clients Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["oauth2-server"],"summary":"Create Oauth2 Client","description":"Register a new OAuth2 client.\n\nIssue #1752: the owner is emailed a security notice after the commit\n(a new client secret was minted on the account).\n\nArgs:\n    data: Client registration data\n\nReturns:\n    Created client with client_secret (only shown once)\n\nExample:\n    POST /api/v1/oauth/clients\n    {\n      \"client_name\": \"My ChatGPT App\",\n      \"redirect_uris\": [\"https://chatgpt.com/connector_platform_oauth_redirect\"]\n    }\n\nSecurity:\n    - client_secret is returned ONLY ONCE on creation\n    - client_secret is SHA256 hashed in database\n    - User must save the secret immediately","operationId":"create_oauth2_client_api_v1_oauth_clients_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientWithSecretResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/register":{"post":{"tags":["oauth2-server"],"summary":"Dynamic Client Registration","description":"Dynamic Client Registration (DCR) for MCP clients.\n\nPublic endpoint for MCP clients to register themselves automatically.\nNo authentication required - this follows MCP specification for DCR.\n\nSecurity controls:\n- Provider whitelist: chatgpt, claude, cursor by redirect hostname; for\n  RFC 8252 loopback redirects, a ``client_name`` naming ChatGPT, Claude,\n  Cursor, Codex, Hermes Agent or OpenClaw (issue #1657). Every entry of\n  ``redirect_uris`` must pass on its own (#1686)\n- IP-based rate limiting (5 registrations per minute per IP); addresses in\n  ``OAUTH_DCR_RATE_LIMIT_EXEMPT_CIDRS`` are not limited (#1741)\n- Redirect URI pattern validation\n- Automatic token_endpoint_auth_method=\"none\" (public clients)\n\nArgs:\n    request: FastAPI request (for IP-based rate limiting)\n    data: DCR request data\n\nReturns:\n    Created client metadata. Per RFC 7591 §3.2.1, public clients\n    (``token_endpoint_auth_method=\"none\"``) do NOT receive a\n    ``client_secret`` — the field is omitted from the JSON response\n    via ``response_model_exclude`` (Issue #689). Clients authenticate\n    the token endpoint using PKCE alone.\n\nExample:\n    POST /api/v1/oauth/register\n    {\n      \"client_name\": \"ChatGPT MCP\",\n      \"redirect_uris\": [\"https://chatgpt.com/connector_platform_oauth_redirect\"],\n      \"token_endpoint_auth_method\": \"none\"\n    }\n\nSpec:\n    RFC 7591 - OAuth 2.0 Dynamic Client Registration Protocol\n    RFC 7636 - PKCE (required for public clients)\n    MCP Authorization Spec (2025-03-26)","operationId":"dynamic_client_registration_api_v1_oauth_register_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DynamicClientRegistrationRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientWithSecretResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/clients/{client_id}":{"get":{"tags":["oauth2-server"],"summary":"Get Oauth2 Client","description":"Get OAuth2 client details.\n\nArgs:\n    client_id: OAuth2 client ID\n\nReturns:\n    Client details (without secret)\n\nExample:\n    GET /api/v1/oauth/clients/oauth_abc123","operationId":"get_oauth2_client_api_v1_oauth_clients__client_id__get","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["oauth2-server"],"summary":"Update Oauth2 Client","description":"Update OAuth2 client.\n\nArgs:\n    client_id: OAuth2 client ID\n    data: Update data\n\nReturns:\n    Updated client\n\nExample:\n    PUT /api/v1/oauth/clients/oauth_abc123\n    {\n      \"client_name\": \"My App (Updated)\",\n      \"redirect_uris\": [\"https://example.com/callback\"]\n    }\n\nNote:\n    - client_secret cannot be updated (create new client if needed)\n    - Can update: name, redirect_uris, scope, token_endpoint_auth_method","operationId":"update_oauth2_client_api_v1_oauth_clients__client_id__put","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["oauth2-server"],"summary":"Delete Oauth2 Client","description":"Delete OAuth2 client.\n\nArgs:\n    client_id: OAuth2 client ID\n\nReturns:\n    No content\n\nExample:\n    DELETE /api/v1/oauth/clients/oauth_abc123\n\nNote:\n    - Deleting a client will CASCADE delete all associated tokens\n    - This action cannot be undone","operationId":"delete_oauth2_client_api_v1_oauth_clients__client_id__delete","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/clients/{client_id}/hide":{"post":{"tags":["oauth2-server"],"summary":"Hide Oauth2 Client Secret","description":"Hide OAuth2 client secret (Owner only).\n\nSets hidden_at timestamp and deletes plaintext_secret_encrypted.\n\nArgs:\n    client_id: OAuth2 client ID\n\nReturns:\n    Status message","operationId":"hide_oauth2_client_secret_api_v1_oauth_clients__client_id__hide_post","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Hide Oauth2 Client Secret Api V1 Oauth Clients  Client Id  Hide Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/clients/{client_id}/regenerate-secret":{"post":{"tags":["oauth2-server"],"summary":"Regenerate Oauth2 Client Secret","description":"Regenerate OAuth2 client secret.\n\nIssue #169: Secret regeneration feature. Issue #1752: the owner is emailed\na security notice after the commit.\n\nWARNING: This immediately invalidates the old secret. Update all applications.\n\nArgs:\n    client_id: OAuth2 client ID\n\nReturns:\n    Updated client with new client_secret (only shown once)\n\nExample:\n    POST /api/v1/oauth/clients/oauth_abc123/regenerate-secret","operationId":"regenerate_oauth2_client_secret_api_v1_oauth_clients__client_id__regenerate_secret_post","parameters":[{"name":"client_id","in":"path","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuth2ClientWithSecretResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/providers":{"get":{"tags":["oauth2-server"],"summary":"List Oauth2 Providers","description":"List configured OAuth2 providers.\n\nReturns:\n    List of OAuth2 providers (Google, GitHub, etc.)\n\nExample:\n    GET /api/v1/oauth/providers\n\nNote:\n    This endpoint returns OAuth2 providers configured in .env file\n    for user login (not OAuth2 clients registered by users).","operationId":"list_oauth2_providers_api_v1_oauth_providers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/OAuth2ProviderResponse"},"type":"array","title":"Response List Oauth2 Providers Api V1 Oauth Providers Get"}}}}}}},"/api/v1/oauth/authorize":{"get":{"tags":["oauth2-server"],"summary":"Oauth Authorize Get","description":"OAuth2 authorization endpoint (consent screen).\n\nBefore the consent screen is shown, the request must pass the rules the\nconsent submission applies (#1686): a grantable ``scope``,\n``code_challenge_method=S256`` (and a ``code_challenge`` from public\nclients), and a ``resource`` naming this server's MCP resource. A failing\nrequest gets an error page (400) showing the OAuth error; nothing is sent\nto the client before the user acts. The screen lists the permissions of\nthe granted scope.\n\nEvery parameter travels to the consent POST in its query string, and the\nPOST validates them again through the grant.","operationId":"oauth_authorize_get_api_v1_oauth_authorize_get","parameters":[{"name":"client_id","in":"query","required":true,"schema":{"type":"string","title":"Client Id"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","title":"Redirect Uri"}},{"name":"response_type","in":"query","required":false,"schema":{"type":"string","default":"code","title":"Response Type"}},{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}},{"name":"resource","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource"}},{"name":"code_challenge","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge"}},{"name":"code_challenge_method","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Code Challenge Method"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["oauth2-server"],"summary":"Oauth Authorize Post","description":"Process authorization consent.\n\nOAuth2 params (client_id, redirect_uri, etc.) must be in query string,\nnot in POST body. This is required by Authlib 1.6.5 which generates\npayload from query params for authorization endpoint.\n\nIssue #1752: the user is emailed a security notice once the\nauthorization code is stored, when the consent grants the client\nsomething new — a first authorization, a scope the user has not granted\nit before, or a client changed (name, redirect URIs, scope) since the\nuser's last grant. An unchanged repeat consent adds little: the code can\nonly reach the client's registered redirect URI.","operationId":"oauth_authorize_post_api_v1_oauth_authorize_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/oauth/token":{"post":{"tags":["oauth2-server"],"summary":"Oauth Token","description":"OAuth2 token endpoint.\n\nAccepts authorization_code or refresh_token grants.\nIssue #157: Support public clients (no client_secret) with PKCE.\nReturns Authlib response with proper status code and headers.","operationId":"oauth_token_api_v1_oauth_token_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/oauth/device/authorize":{"post":{"tags":["oauth2-server"],"summary":"Device Authorize","description":"Device Authorization endpoint (RFC 8628 Section 3.1).\n\nCalled by CLI clients to obtain a device_code + user_code pair.\nNo authentication required — this is a public endpoint.\n\nLimited per client address to ``oauth_device_authorize_rate_limit_per_minute``\nrequests per minute (#1656). Over the limit it returns 429 in the RFC 6749\n§5.2 error shape (RFC 8628 §3.2) with ``Retry-After``, before any row is\nwritten.\n\nThe body is ``application/x-www-form-urlencoded`` (RFC 8628 §3.1) or\n``application/json`` (#1671). It is read after the limit is counted, so a\nrefused body still counts. A missing or unsupported Content-Type, a\nmalformed body or a missing ``client_id`` returns 400 ``invalid_request``,\nan oversized body returns 413 ``invalid_request``, an unknown ``client_id``\nreturns 400 ``invalid_client``, an admin-managed client registered without a\nmemory scope returns 400 ``invalid_scope`` and a failure to store the grant\nreturns 500 ``server_error``, all in the RFC 6749 §5.2 error shape.\n\nThe granted scope follows ``granted_scope`` (#1686), as at ``/authorize``.","operationId":"device_authorize_api_v1_oauth_device_authorize_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"properties":{"client_id":{"type":"string","title":"Client Id","description":"OAuth2 client identifier"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope","description":"Requested scope (space-separated)"}},"type":"object","required":["client_id"],"title":"DeviceAuthorizationRequest","description":"Device Authorization Request (RFC 8628 Section 3.1).\n\nSent as application/x-www-form-urlencoded (RFC 8628 §3.1) or application/json."}},"application/json":{"schema":{"properties":{"client_id":{"type":"string","title":"Client Id","description":"OAuth2 client identifier"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope","description":"Requested scope (space-separated)"}},"type":"object","required":["client_id"],"title":"DeviceAuthorizationRequest","description":"Device Authorization Request (RFC 8628 Section 3.1).\n\nSent as application/x-www-form-urlencoded (RFC 8628 §3.1) or application/json."}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationResponse"}}}},"400":{"description":"RFC 6749 §5.2 error: `invalid_request` for a missing or unsupported Content-Type, a malformed body or a missing `client_id`; `invalid_client` for an unknown `client_id`; `invalid_scope` for an admin-managed client registered without a memory scope","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error","error_description"]}}}},"413":{"description":"Request body larger than 4096 bytes (`invalid_request`)","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error","error_description"]}}}},"429":{"description":"Too many device authorization requests from this address","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error","error_description"]}}}},"500":{"description":"The device authorization could not be stored (`server_error`)","content":{"application/json":{"schema":{"properties":{"error":{"type":"string"},"error_description":{"type":"string"}},"type":"object","required":["error","error_description"]}}}}}}},"/api/v1/oauth/device/verify":{"post":{"tags":["oauth2-server"],"summary":"Device Verify","description":"Look up a pending device authorization by user_code.\n\nReturns enough information for the browser consent screen to render\n(client_name, scope). No authentication required — possession of the\nuser_code is the bearer token for this lookup.\n\nLimited per client address to ``oauth_device_verify_rate_limit_per_minute``\nrequests per minute, found or not (RFC 8628 §5.1, #1656). Over the limit it\nreturns 429 with ``Retry-After`` before the lookup runs.","operationId":"device_verify_api_v1_oauth_device_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceVerifyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceVerifyResponse"}}}},"429":{"description":"Too many user code lookups from this address"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/device/confirm":{"post":{"tags":["oauth2-server"],"summary":"Device Confirm","description":"User consent endpoint for device authorization.\n\nRequires session authentication. Sets authorized_at or denied_at on the\ndevice code record so the polling CLI receives the decision. Every\napproval emails the user a security notice after the commit (Issue #1752):\na device code can be phished (the attacker starts the flow and gets the\nvictim to approve the code), so unlike authorization-code consent this is\nnot limited to the first grant; coalescing bounds the volume. Scheduling\nthe notice touches no DB, so nothing here runs sync DB work for it.\n\nThe database work runs in a worker thread (#1770): the approval\nshare-locks the owner's ``users`` row and may wait on a reset holding it.","operationId":"device_confirm_api_v1_oauth_device_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/introspect":{"post":{"tags":["oauth2-server"],"summary":"Introspect Token","description":"Token Introspection endpoint (RFC 7662).\n\nIssue #157: MCP SDK compliance - Token Introspection.\n\nThe caller must authenticate as a confidential OAuth client, with HTTP\nBasic (``client_secret_basic``) or ``client_id`` + ``client_secret`` in\nthe form (``client_secret_post``) (RFC 7662 §2.1, #1741). Anyone else,\nincluding a public client, gets ``401 invalid_client``. A client learns\nabout its own tokens only: a token issued to another client, like an\nunknown, expired or revoked one, is ``{\"active\": false}``.\n\nArgs:\n    request: FastAPI request (Authorization header, caller IP logging)\n    token: Access token to introspect\n    client_id: Caller's client_id for ``client_secret_post``\n    client_secret: Caller's secret for ``client_secret_post``\n\nReturns:\n    Token metadata if active and issued to the caller, else\n    ``{\"active\": false}``; a 401 error response when the caller is not\n    an authenticated confidential client.\n\nExample:\n    POST /api/v1/oauth/introspect\n    Authorization: Basic <base64(client_id:client_secret)>\n    Content-Type: application/x-www-form-urlencoded\n\n    token=mcp_abc123...\n\nSpec: RFC 7662 - OAuth 2.0 Token Introspection","operationId":"introspect_token_api_v1_oauth_introspect_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_introspect_token_api_v1_oauth_introspect_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenIntrospectionResponse"}}}},"400":{"description":"Two client authentication methods in one request"},"401":{"description":"Missing or invalid client credentials, or a public client (``invalid_client``)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/oauth/revoke":{"post":{"tags":["oauth2-server"],"summary":"Oauth Revoke","description":"Revoke an access or refresh token (RFC 7009).\n\nThe caller identifies itself (#1741, RFC 7009 §2.1): a public client by\n``client_id``, a confidential client with its secret by HTTP Basic or\nthe form. A token issued to another client is left alone and answered\nlike an unknown token, ``200`` (§2.2). Revoking a refresh token also\nrevokes the access token issued with it.","operationId":"oauth_revoke_api_v1_oauth_revoke_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_oauth_revoke_api_v1_oauth_revoke_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"400":{"description":"Two client authentication methods in one request"},"401":{"description":"Missing client_id, unknown client, or bad secret"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/api-keys":{"get":{"tags":["api-keys"],"summary":"List Api Keys","description":"List API keys for the current user.\n\nIssue #246: Returns all user's API keys (no context filtering).","operationId":"list_api_keys_api_v1_config_api_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/APIKeyResponse"},"title":"Response List Api Keys Api V1 Config Api Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["api-keys"],"summary":"Create Api Key","description":"Create a new API key for the current user.\n\nIssue #246: Creates API key with context_id=None (no auto-assignment).\nIssue #1752: the owner is emailed a security notice once the key is\ncommitted (the key's name only, never its value).","operationId":"create_api_key_api_v1_config_api_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyCreate"}}}},"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/v1/config/api-keys/{key_id}":{"delete":{"tags":["api-keys"],"summary":"Delete Api Key","description":"Permanently delete an API key.\n\nIssue #112: Delete by ID directly to avoid ambiguity with duplicate names across contexts.\n\nWARNING: Hard delete. Use POST /{key_id}/revoke for soft delete.","operationId":"delete_api_key_api_v1_config_api_keys__key_id__delete","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["api-keys"],"summary":"Update Api Key","description":"Set or clear the key's MCP default context (#2129).\n\nThe only field this route changes. A context-scoped MCP tool called with\nthis key and without ``context_id`` runs in the default (``?context=``\non the URL wins). The context must be readable by the key's owner now,\nwithin the key's workspace scope; otherwise 404 ``Context not found``\n(the same answer for a context that does not exist). Public-bound and\nagent-bound keys cannot carry one (422): an agent key's default is its\nagent's default binding.\n\nRaises:\n    NotFoundException: The key is not the caller's, or the context is not\n        readable (404).\n    BadRequestError: The key is revoked (400).\n    ValidationError: The key is public-bound, agent-bound or a connector's\n        shared key (422).","operationId":"update_api_key_api_v1_config_api_keys__key_id__patch","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/api-keys/{key_id}/revoke":{"post":{"tags":["api-keys"],"summary":"Revoke Api Key","description":"Revoke an API key (soft delete, preserves audit trail).\n\nIssue #112: Revoke by ID directly to avoid ambiguity with duplicate names.","operationId":"revoke_api_key_api_v1_config_api_keys__key_id__revoke_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/api-keys/{key_id}/regenerate":{"post":{"tags":["api-keys"],"summary":"Regenerate Api Key","description":"Regenerate an API key (revokes old key, creates new one with same name).\n\nIssue #169: Secret regeneration feature. Issue #1752: the owner is emailed\na security notice after the commit.\n\nWARNING: This immediately invalidates the old key. Update all applications.","operationId":"regenerate_api_key_api_v1_config_api_keys__key_id__regenerate_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/api-keys/{key_id}/stats":{"get":{"tags":["api-keys"],"summary":"Get Api Key Stats","description":"Get API key usage statistics.","operationId":"get_api_key_stats_api_v1_config_api_keys__key_id__stats_get","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/APIKeyStats"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/share-keys":{"post":{"tags":["share-keys"],"summary":"Create Share Key","description":"Mint a read-only, TTL-bounded share key for one owned context (#1027).\n\nThe caller must OWN ``context_id`` — enforced via\n``check_context_access(..., OWNER)``, which raises a uniform 404 if the\ncontext does not exist, and 403 if the caller lacks owner access (whether\nthey are a non-member or a member without owner rights — the 403 does not\ndistinguish, to avoid leaking membership). This prevents minting a share\nkey for a context the user cannot administer.","operationId":"create_share_key_api_v1_config_share_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareKeyCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShareKeyCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["share-keys"],"summary":"List Share Keys","description":"List the current user's share keys (newest first).","operationId":"list_share_keys_api_v1_config_share_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ShareKeyResponse"},"title":"Response List Share Keys Api V1 Config Share Keys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/share-keys/{key_id}/revoke":{"post":{"tags":["share-keys"],"summary":"Revoke Share Key","description":"Revoke a share key (soft delete; uniform 404 if not the user's active key).","operationId":"revoke_share_key_api_v1_config_share_keys__key_id__revoke_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^skey_[0-9A-Za-z]{22}$","description":"Public id of the share key (`skey_` + 22 base62 characters).","examples":["skey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the share key (`skey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/share/recall":{"post":{"tags":["share-keys"],"summary":"Share Recall","description":"Recall confined to the share key's bound context (#1027).\n\nThe bound context (and its workspace) come from the verified share key —\nnever from the request or the owner's current workspace. A client-supplied\n``filters.context_id`` that differs from the bound context is rejected\n(BOUND_SCOPE_VIOLATION), and otherwise recall is forced to the bound\ncontext so cross-context read is structurally impossible (#963/#150).","operationId":"share_recall_api_v1_share_recall_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/share/sessions":{"get":{"tags":["share-keys"],"summary":"Share Sessions","description":"List the bound context's live agent session-state, read-only (#1064).\n\nA **read-only projection** of the #889/#906 agent session-state lane,\ndriven by a #1027 share key and confined to the key's bound context — so an\nexternal, observe-only dashboard can render live agent status without\nholding a privileged credential and without the agent dialling out.\n\nConfinement and read-only-ness are structural (inherited from #1027): the\ncontext comes from the verified share key (never the request), and write /\ncontrol verbs on the lane (`PUT`/`DELETE /contexts/{id}/state`) authenticate\nagainst `api_keys` — a share key is rejected there outright. Approvals stay\non the cockpit/Slack operator path; this surface can only *observe*.","operationId":"share_sessions_api_v1_share_sessions_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharedSessionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/pubkeys":{"post":{"tags":["secrets"],"summary":"Register Pubkey","description":"Register the caller's own age recipient pubkey (pending owner approval).","operationId":"register_pubkey_api_v1_config_secrets_pubkeys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyRegister"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["secrets"],"summary":"List Pubkeys","description":"List all recipient pubkeys in the workspace (owner/admin approval console).","operationId":"list_pubkeys_api_v1_config_secrets_pubkeys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PubkeyResponse"},"title":"Response List Pubkeys Api V1 Config Secrets Pubkeys Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/pubkeys/me":{"get":{"tags":["secrets"],"summary":"List My Pubkeys","description":"List the caller's own recipient pubkeys (e.g. to check approval status).","operationId":"list_my_pubkeys_api_v1_config_secrets_pubkeys_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PubkeyResponse"},"title":"Response List My Pubkeys Api V1 Config Secrets Pubkeys Me Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/pubkeys/{pubkey_id}/approve":{"post":{"tags":["secrets"],"summary":"Approve Pubkey","description":"Owner-approve a pending pubkey → active (the TOFU trust gate).","operationId":"approve_pubkey_api_v1_config_secrets_pubkeys__pubkey_id__approve_post","parameters":[{"name":"pubkey_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Pubkey Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/pubkeys/{pubkey_id}/revoke":{"post":{"tags":["secrets"],"summary":"Revoke Pubkey","description":"Owner-revoke a pubkey; revokes its grants and flags rotation on affected secrets.","operationId":"revoke_pubkey_api_v1_config_secrets_pubkeys__pubkey_id__revoke_post","parameters":[{"name":"pubkey_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Pubkey Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PubkeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets":{"post":{"tags":["secrets"],"summary":"Put Secret","description":"Store a new ciphertext version + reconcile grants. Server never sees plaintext.","operationId":"put_secret_api_v1_config_secrets_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretPut"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretPutResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["secrets"],"summary":"List Secrets","description":"List secret names + metadata. Never returns ciphertext/values.","operationId":"list_secrets_api_v1_config_secrets_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SecretMetaResponse"},"title":"Response List Secrets Api V1 Config Secrets Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/audit/verify":{"get":{"tags":["secrets"],"summary":"Verify Audit Chain","description":"Recompute the workspace's secret-access audit chain and report integrity.\n\nOwner/admin ops surface that makes the tamper-evidence usable in-product:\nwalks the append-only log id-ascending, recomputes each HMAC ``entry_hash``\nand verifies the ``prev_hash`` linkage. Returns ``valid: true`` with the head\nhash, or ``valid: false`` with the first ``broken_at`` id and ``reason``.","operationId":"verify_audit_chain_api_v1_config_secrets_audit_verify_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditVerifyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/revoke-grant":{"post":{"tags":["secrets"],"summary":"Revoke Grant","description":"Revoke one recipient's grant; sets rotation_needed (revoke ≠ un-share).","operationId":"revoke_grant_api_v1_config_secrets_revoke_grant_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeGrant"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretMetaResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/{name}":{"delete":{"tags":["secrets"],"summary":"Delete Secret","description":"Owner-only hard-delete of a secret + its versions/grants (#1153).\n\nDelete is **cleanup, not a security control**: it removes superseded\nciphertext at rest but does NOT un-share a value a recipient already fetched,\nnor rotate the still-live upstream credential. Rotate the upstream key first,\nthen delete (the UI confirmation warns about this). A ``delete`` entry is\nappended to the tamper-evident audit chain before removal, and the audit\nhistory of the deleted secret survives (the log is FK-decoupled), so\n``audit/verify`` still passes afterwards.\n\n``{name:path}`` so slash-containing names (e.g. ``cloudflare/api-token``) are\naddressable. Owner-only mirrors the pubkey trust-gate ops (approve/revoke):\na non-owner (incl. a non-owner admin), member, or viewer is rejected 403.","operationId":"delete_secret_api_v1_config_secrets__name__delete","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/secrets/fetch":{"post":{"tags":["secrets"],"summary":"Fetch Secret","description":"Return ciphertext to a granted caller (default-deny, audit-first).\n\nThe service writes and commits a fail-closed audit entry before the\nciphertext is returned; a denied read raises 403 after logging. The returned\nciphertext is opaque — the server holds no key to decrypt it.","operationId":"fetch_secret_api_v1_config_secrets_fetch_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretFetch"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecretValueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/agents":{"post":{"tags":["agents"],"summary":"Register Agent","description":"Register an agent in the active workspace (owner/admin only).","operationId":"register_agent_api_v1_agents_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["agents"],"summary":"List Agents","description":"List the active workspace's registered agents (owner/admin only).","operationId":"list_agents_api_v1_agents_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/agents/{agent_id}":{"get":{"tags":["agents"],"summary":"Get Agent","description":"Fetch one registered agent (owner/admin only).","operationId":"get_agent_api_v1_agents__agent_id__get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["agents"],"summary":"Update Agent","description":"Partially update an agent, including status / enforcement transitions.","operationId":"update_agent_api_v1_agents__agent_id__patch","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["agents"],"summary":"Delete Agent","description":"Hard-delete a registry row (owner/admin only).\n\nOperational retirement should normally use ``status='retired'``; hard\ndelete stays available and (from P0-2 on) cascades every bound key.","operationId":"delete_agent_api_v1_agents__agent_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/agents/{agent_id}/bindings":{"post":{"tags":["agents"],"summary":"Create Agent Binding","description":"Bind an agent to a context (owner/admin only, purely subtractive).","operationId":"create_agent_binding_api_v1_agents__agent_id__bindings_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BindingCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BindingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["agents"],"summary":"List Agent Bindings","description":"List an agent's context bindings (owner/admin only).","operationId":"list_agent_bindings_api_v1_agents__agent_id__bindings_get","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BindingListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/agents/{agent_id}/bindings/{binding_id}":{"patch":{"tags":["agents"],"summary":"Update Agent Binding","description":"Partially update a binding (owner/admin only).","operationId":"update_agent_binding_api_v1_agents__agent_id__bindings__binding_id__patch","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"binding_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Binding Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BindingUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BindingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["agents"],"summary":"Delete Agent Binding","description":"Delete a binding (owner/admin only) — the agent loses that context.","operationId":"delete_agent_binding_api_v1_agents__agent_id__bindings__binding_id__delete","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"binding_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Binding Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/agents/{agent_id}/bootstrap":{"post":{"tags":["agents"],"summary":"Agent Bootstrap","description":"Rehydrate an agent's cognitive state at session start (#1276).\n\nAuth is ``APIKeyOrSessionUser``; the per-request agent scope (set at\nAPI-key verify for agent-bound keys) drives the identity rule inside the\nservice. Component failures are fail-soft; identity/authorization failures\nare total and fail-closed with the uniform ``agent_not_found`` /\n``context_not_found`` shapes.","operationId":"agent_bootstrap_api_v1_agents__agent_id__bootstrap_post","parameters":[{"name":"agent_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Agent Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BootstrapRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Agent Bootstrap Api V1 Agents  Agent Id  Bootstrap Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users":{"get":{"tags":["admin"],"summary":"List Users","description":"List all users with their memory statistics.\n\nAdmin-only endpoint.\n\nIssue #164: Extended with search/filter capabilities and workspace info.\n\nArgs:\n    user: Authenticated admin user\n    db: Database session\n    limit: Maximum number of users to return\n    offset: Pagination offset\n    include_workspaces: Include workspace membership info\n    search: Search by email or name (partial match)\n    workspace_id: Filter by workspace ID\n    role: Filter by system role (admin/user)\n    plan: Filter by plan tier (free/basic/pro/promax)\n    sort: Sort field (created_at, last_login, memory_count)\n\nReturns:\n    List of users with memory counts, storage usage, and optionally workspace info","operationId":"list_users_api_v1_admin_users_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":100,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"include_workspaces","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Workspaces"}},{"name":"search","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Search"}},{"name":"workspace_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Id"}},{"name":"role","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role"}},{"name":"plan","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan"}},{"name":"sort","in":"query","required":false,"schema":{"type":"string","default":"created_at","title":"Sort"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/stats":{"get":{"tags":["admin"],"summary":"Get User Stats","description":"Get detailed statistics for a specific user.\n\nAdmin-only endpoint.\n\nArgs:\n    user_id: Target user ID\n    admin: Authenticated admin user\n    db: Database session\n\nReturns:\n    Detailed user statistics including memory breakdown","operationId":"get_user_stats_api_v1_admin_users__user_id__stats_get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserStats"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}":{"get":{"tags":["admin"],"summary":"Get User Detail","description":"Get comprehensive user details including workspaces, contexts, and stats.\n\nIssue #164: User detail page.\n\nArgs:\n    user_id: Target user ID (OAuth2 user_id)\n    admin: Authenticated admin user\n    db: Database session\n\nReturns:\n    UserDetailResponse with user info, workspaces, accessible contexts, and stats","operationId":"get_user_detail_api_v1_admin_users__user_id__get","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["admin"],"summary":"Delete User","description":"Delete a user and all their data (Admin-only).\n\nProtection:\n- Cannot delete initial admin (is_initial_admin=True)\n- Cannot delete last remaining admin\n- Cannot delete your own account\n\nIssue #166: System Admin protection\n\nDeletes memories, API keys, OAuth2 clients, Qdrant collection, and user account.","operationId":"delete_user_api_v1_admin_users__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/role":{"put":{"tags":["admin"],"summary":"Update User Role","description":"Update user role (Admin-only).","operationId":"update_user_role_api_v1_admin_users__user_id__role_put","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RoleUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/workspace_slot_bonus":{"patch":{"tags":["admin"],"summary":"Update Workspace Slot Bonus","description":"Apply a signed delta to a user's workspace_slot_bonus (Admin-only).\n\nIssue #676. Atomic via ``UPDATE ... RETURNING`` so two admins clicking\n+1 simultaneously cannot overwrite each other (read-modify-write at the\nORM layer would race). The DB CHECK constraint\n``workspace_slot_bonus_nonneg`` is the ultimate safety net; the\napp-level ``BonusBelowZeroError`` exists so SDK consumers receive a\nstructured 400 (``BONUS-002``) instead of a generic IntegrityError.\n\n``reason`` is required only when the new cap would fall below the\nuser's current owned_count — admin can still revoke bonus from users\nnot at risk of over-cap without filling in a reason every time.\n\nAll mutations write a row to ``audit_logs`` with the canonical\n``user_metadata`` JSON payload pattern (matches\n``system_admin_service.py``); SHA256 hashing of the integer values is\nunnecessary so they are stored as plain strings in ``old_value_hash`` /\n``new_value_hash`` for the rare grep-by-numeric-value query.","operationId":"update_workspace_slot_bonus_api_v1_admin_users__user_id__workspace_slot_bonus_patch","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkspaceSlotBonusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWorkspaceSlotBonusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/users/{user_id}/erase":{"post":{"tags":["admin"],"summary":"Admin Force Erase User","description":"Force-erase a user account (admin-only, GDPR Art.17 / APPI compliance).\n\nSkips the 7-day cooling-off period: the deletion executes immediately.\nCross-store cleanup (Stripe + Qdrant + Postgres + Redis) is handled\nby AccountErasureService following the same orchestration as the\nself-service path.\n\nRequired body:\n    reason_code: one of user_request_via_support / legal_order /\n        inactivity_policy / abuse_violation / other\n    reason_detail: free-form admin note (max 1000 chars)\n\nProtections (mirrors SystemAdminService.can_delete_admin):\n    - 403 if target is the initial admin\n    - 403 if target is the last remaining admin\n    - 403 if admin tries to erase their own account\n    - 409 if an erasure request is already in flight for this user\n    - 409 if the target owns a shared workspace and no alternate\n      admin exists for auto-transfer\n\nThis endpoint replaces the older `DELETE /admin/users/{user_id}` for\nnew integrations. The old route is kept temporarily for backward\ncompatibility but does NOT clean Qdrant or all OAuth artifacts.","operationId":"admin_force_erase_user_api_v1_admin_users__user_id__erase_post","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminErasureRequestBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Admin Force Erase User Api V1 Admin Users  User Id  Erase Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/workspaces/{workspace_id}/force-transfer-ownership":{"post":{"tags":["admin"],"summary":"Force Transfer Workspace Ownership","description":"Break-glass: a system admin force-transfers workspace ownership (#1101).\n\nFor when the current owner is unavailable. System-admin-gated\n(``require_admin``), bound to the PATH ``workspace_id``, serialized on the\nshared workspace row lock, bumps ``ownership_epoch`` (invalidates the displaced\nowner's billing-handoff tokens / sessions — #1100), and writes a MANDATORY\naudit row (``break_glass`` + ``reason`` + acting admin) atomically with the\ntransfer. The target need not be a member — a non-member is added as OWNER,\nwhich handles the zero-member workspace. The displaced previous owner is\nnotified best-effort.\n\nDual-control (#1113): when ``require_dual_control_force_transfer`` is enabled,\nthis does NOT seize ownership immediately — it files a PENDING request and\nreturns ``202`` with a ``request_id`` that a SECOND, distinct system admin\nmust approve via ``/admin/force-transfer-requests/{request_id}/approve``.\nDefault config keeps the immediate single-control behavior.","operationId":"force_transfer_workspace_ownership_api_v1_admin_workspaces__workspace_id__force_transfer_ownership_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceTransferOwnershipRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Force Transfer Workspace Ownership Api V1 Admin Workspaces  Workspace Id  Force Transfer Ownership Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/force-transfer-requests/{request_id}/approve":{"post":{"tags":["admin"],"summary":"Approve Force Transfer Request","description":"Dual-control (#1113): a SECOND system admin approves a pending force-transfer.\n\nSystem-admin-gated. The approver MUST be a different admin than the initiator\n(enforced in the service → 400). On approval the seizure applies atomically\nwith a mandatory audit recording BOTH identities, bumps ``ownership_epoch``,\nand the displaced previous owner is notified best-effort. Refused with 409 if\nthe request is not pending, or if the workspace's ownership moved since the\nrequest was filed (stale — re-initiate).","operationId":"approve_force_transfer_request_api_v1_admin_force_transfer_requests__request_id__approve_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Approve Force Transfer Request Api V1 Admin Force Transfer Requests  Request Id  Approve Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/force-transfer-requests/{request_id}/cancel":{"post":{"tags":["admin"],"summary":"Cancel Force Transfer Request","description":"Dual-control (#1113): retract a pending force-transfer request.\n\nSystem-admin-gated. Lets an admin cancel a pending request (e.g. filed in\nerror) without having to initiate a fresh one to supersede it. Refused with\n409 if the request is not pending.","operationId":"cancel_force_transfer_request_api_v1_admin_force_transfer_requests__request_id__cancel_post","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Cancel Force Transfer Request Api V1 Admin Force Transfer Requests  Request Id  Cancel Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/force-transfer-requests/{request_id}":{"get":{"tags":["admin"],"summary":"Get Force Transfer Request","description":"Dual-control (#1113): inspect a force-transfer request before approving.\n\nSystem-admin-gated read so the SECOND admin can verify the target, reason,\nand initiator before calling ``/approve`` — making four-eyes an informed\nreview rather than a blind second button-press. 404 if no such request.","operationId":"get_force_transfer_request_api_v1_admin_force_transfer_requests__request_id__get","parameters":[{"name":"request_id","in":"path","required":true,"schema":{"type":"string","title":"Request Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Force Transfer Request Api V1 Admin Force Transfer Requests  Request Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/embedding/retry-failed":{"post":{"tags":["admin"],"summary":"Retry Failed Embeddings","description":"Reset failed embeddings to pending for automatic retry.\n\nIssue #93: Admin tool to recover from embedding failures.","operationId":"retry_failed_embeddings_api_v1_admin_embedding_retry_failed_post","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by context ID (UUID)","title":"Context Id"},"description":"Filter by context ID (UUID)"},{"name":"workspace_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by workspace ID (UUID)","title":"Workspace Id"},"description":"Filter by workspace ID (UUID)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Retry Failed Embeddings Api V1 Admin Embedding Retry Failed Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/contexts/{context_id}/restore":{"post":{"tags":["admin"],"summary":"Restore Context","description":"Restore a soft-deleted context and the memories its deletion tombstoned.\n\n#1804: clears ``deleted_at`` on the context and on the memories deleted\nwith it, and marks those memories ``pending`` so the embedding sweep\nrebuilds their vectors (about 2,400 an hour; until then recall does not\nfind them). Memories forgotten before the deletion,\nSleep tombstones and rows the tombstone purge already removed stay gone,\nas do the context's neural edges, members' ``allowed_context_ids``\nentries and revoked resource tokens.\n\nDefault is dry_run=True. 404 for a context with no row (see\n``/contexts/recover``), 409 for one that is not deleted, is in a deleted\nworkspace, or whose name or resource a live context now uses.","operationId":"restore_context_api_v1_admin_contexts__context_id__restore_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextRestoreRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextRestoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/contexts/recover":{"post":{"tags":["admin"],"summary":"Recover Context","description":"Recover a deleted context from surviving Qdrant data.\n\nIssue #86: Admin endpoint to reconstruct context and memory records\nfrom Qdrant point payloads when a context has been accidentally deleted.\n\n#1798: deleting a context now removes its points, and the orphan sweep\nremoves points left by earlier deletions, so this finds nothing for a\ncontext deleted on v0.88.0 or later. Its soft-deleted rows stay in\nPostgres until the tombstone purge; ``POST /contexts/{context_id}/restore``\n(#1804) brings it back from them.\n\nDefault is dry_run=True — shows what would be recovered without making changes.","operationId":"recover_context_api_v1_admin_contexts_recover_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextRecoveryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextRecoveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/plans/workspaces":{"get":{"tags":["admin-plans"],"summary":"List Workspaces With Plans","description":"List all workspaces with plan info and usage stats.\n\nAdmin-only endpoint - shows all workspaces in the system.\n\nReturns:\n    List of all workspaces with plan tiers and current usage","operationId":"list_workspaces_with_plans_api_v1_admin_plans_workspaces_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AdminWorkspacePlanInfo"},"type":"array","title":"Response List Workspaces With Plans Api V1 Admin Plans Workspaces Get"}}}}}}},"/api/v1/admin/plans/tiers":{"get":{"tags":["admin-plans"],"summary":"List Plan Tiers","description":"List all plan tier configurations.\n\nIssue #664: Admin tiers tab consumes this endpoint instead of i18n\nhardcoded values, so values stay in sync with environment overrides\n(``PLAN_FREE_MEMORY_LIMIT`` etc.). No DB access — ``PLAN_TIERS`` is a\nprocess-global registry populated at import time.\n\nReturns:\n    List of plan tier info in canonical upgrade order (``PLAN_ORDER``).","operationId":"list_plan_tiers_api_v1_admin_plans_tiers_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/PlanTierInfo"},"type":"array","title":"Response List Plan Tiers Api V1 Admin Plans Tiers Get"}}}}}}},"/api/v1/admin/plans/workspaces/{workspace_id}/plan":{"put":{"tags":["admin-plans"],"summary":"Update Workspace Plan","description":"Change workspace plan tier.\n\nAdmin-only endpoint.\n\nArgs:\n    workspace_id: Workspace ID\n    request: Update plan request with new plan name and optional reason\n\nReturns:\n    ``message``, and ``content_chunks_deactivated``: the ids of the\n    contexts whose content chunk vectors go inactive under the new plan\n    (#2103; their flag and chunk points are kept; empty where\n    CONTENT_CHUNKS_REQUIRE_PLAN is off), and ``llm_selector_deactivated``:\n    the ids of the contexts whose recall LLM selector goes inactive\n    (#2144; their flag is kept; empty where LLM_SELECTOR_REQUIRE_PLAN is\n    off)\n\nRaises:\n    404: Workspace not found\n    422: Invalid plan tier","operationId":"update_workspace_plan_api_v1_admin_plans_workspaces__workspace_id__plan_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminUpdatePlanRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/plans/audit":{"get":{"tags":["admin-plans"],"summary":"Get Plan Change Audit","description":"Get plan change audit log.\n\nAdmin-only endpoint.\n\nArgs:\n    limit: Maximum entries to return (default: 100)\n\nReturns:\n    List of plan change audit entries","operationId":"get_plan_change_audit_api_v1_admin_plans_audit_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlanChangeAuditEntry"},"title":"Response Get Plan Change Audit Api V1 Admin Plans Audit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/plans/addon-cache-consistency":{"get":{"tags":["admin-plans"],"summary":"Get Addon Cache Consistency","description":"Detect workspaces whose addon cache columns drifted from the row SSoT.\n\nIssue #799. For every active (non-deleted) workspace and every addon type\nin ``_ADDON_FIELD_SPECS``, compares the cached ``addon_*_bonus`` column to\n``SUM(active WorkspaceAddon.quantity) × unit_value`` — the same invariant\nenforced by ``AddonCalculatorService.recalculate_workspace_bonuses`` and\nasserted by ``tests/integration/_addon_helpers.assert_addon_invariant``.\n\nOn-demand audit, not a startup probe: the ``e23_799`` migration brings the\nlive count to 0, so a per-boot full-table scan (all workspaces × 9 columns)\nwould be wasted work. Each drifted column is logged at WARNING and returned\nfor admin review.\n\nReturns:\n    List of drifted (workspace, addon_type) entries. Empty when healthy.","operationId":"get_addon_cache_consistency_api_v1_admin_plans_addon_cache_consistency_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AddonCacheDriftEntry"},"type":"array","title":"Response Get Addon Cache Consistency Api V1 Admin Plans Addon Cache Consistency Get"}}}}}}},"/api/v1/admin/plans/workspaces/{workspace_id}/quotas":{"get":{"tags":["admin-plans"],"summary":"Get Workspace Quotas","description":"Get detailed quota breakdown for a workspace.\n\nReturns base plan limits, addon bonuses, effective totals, and current usage.","operationId":"get_workspace_quotas_api_v1_admin_plans_workspaces__workspace_id__quotas_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceQuotaDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["admin-plans"],"summary":"Update Workspace Quotas","description":"Update workspace addon bonuses via WorkspaceAddon row UPSERT (#665).\n\nPre-#665 this handler wrote ``Workspace.addon_*_bonus`` cache columns\ndirectly, bypassing the #570 contract. Once Stripe addon webhooks\nland, the cache recalc would silently overwrite admin grants. This\nhandler now UPSERTs ``WorkspaceAddon`` rows with\n``source='admin_grant'`` for each touched addon type, then calls\n``AddonCalculatorService.recalculate_workspace_bonuses`` so the\ncache is regenerated from the union of Stripe rows + admin grants.\n\nPer LD-3: no ``db_transaction`` wrapper. The recalc service performs\nits own atomic commit that flushes the staged UPSERTs together. The\nvalidation pass below raises BEFORE any mutation, so there is no\nrollback target between mutation and recalc — ``db_transaction`` was\nonly an error-boundary rollback, never an outer commit boundary\n(``utils/db_helpers.py:45-82``).\n\nPer LD-4 (revised by review-fix #2): all 9 request fields are\n``int | None = None`` with no-touch semantics — omit a field to leave\nits admin grant untouched; send explicit ``0`` to zero it out. The\npre-#665 legacy 5-field admin UI continues to work because it always\nsends all 5 values explicitly. The unified optional shape closes the\npartial-update footgun where omitted-default 0 silently DELETEd\nexisting admin_grant rows.\n\nPer LD-2: absolute-value semantics. For each touched field, compute\n``non_admin_total = SUM(active WorkspaceAddon rows WHERE source !=\n'admin_grant') * unit_value`` and reject (HTTP 400) when the request\nwould require a negative admin grant — admin reductions cannot\nsilently fall below the Stripe-purchased floor. The admin sees the\nconflict instead of getting an unexpected effective value.\n\nPer LD-7: persistent-addon overflow guard (member / context / memory\n/ sleep_contexts). Reject (HTTP 400) when the new effective limit\nfalls below current usage. Daily-reset quotas (mcp / rest / public /\nanalysis) are intentionally NOT guarded — admin throttling mid-day\nis acceptable since the counter resets at next reset boundary. The\n``addon_storage_bonus_mb`` guard is also deferred: per-workspace\nstorage usage tracking does not yet exist in the codebase. Filed as\na follow-up to add the storage guard once usage tracking lands.\n\nPer LD-9: ``extra_sleep_contexts`` PRO-tier check is intentionally\nNOT enforced here. ``_zero_floor`` clamps the effective limit to 0\nfor tiers with base ``sleep_enabled_contexts_limit == 0`` (FREE,\nBASIC), so an admin grant on those tiers is harmless to user-facing\nbehavior; the admin UI (#663) should display \"no effect on this tier\"\nrather than rejecting the request from this handler.\n\nPer #1561: likewise, an ``extra_connectors`` grant on a tier without the\n``connectors`` feature (M/L since #1551) is NOT rejected — admins may\npre-grant ahead of an upgrade. The grant is stored as usual and the\nresponse carries ``warnings: [\"connectors_feature_missing\"]`` (plus a\nstructured log) so the admin UI can flag that the seats are inert until\nthe workspace is upgraded. ``warnings`` is always present (``[]`` when\nnone apply).","operationId":"update_workspace_quotas_api_v1_admin_plans_workspaces__workspace_id__quotas_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAddonRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/plans/workspaces/{workspace_id}/spend-cap":{"put":{"tags":["admin-plans"],"summary":"Update Workspace Spend Cap","description":"Update the per-workspace embedding spend cap override (Issue #709).\n\nSetting a field to ``None`` removes the override and falls back to the\ntier default. Setting a numeric value (>= 0) overrides the tier default\nfor THIS workspace only. The handler rejects override values above the\nworkspace's current tier default — admins lift the cap by upgrading the\nplan tier (or via ``plan_*_embedding_*_cap_usd`` env vars), not by\nraising individual workspaces past their tier ceiling.\n\nConcrete values are written through the SQLAlchemy ORM; the underlying\n``CHECK (embedding_*_cap_usd >= 0)`` constraint in migration\n``e16_709`` is the database-level backstop should the route validation\never be bypassed.","operationId":"update_workspace_spend_cap_api_v1_admin_plans_workspaces__workspace_id__spend_cap_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSpendCapRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/system-admins":{"get":{"tags":["system-admins"],"summary":"List System Admins","description":"List all system administrators.\n\nRequires: System Admin role\n\nReturns:\n    List of system admins with is_initial_admin flags and statistics\n\nExample response:\n    {\n      \"admins\": [\n        {\n          \"email\": \"admin@example.com\",\n          \"user_id\": \"google-oauth2|123456\",\n          \"name\": \"Admin User\",\n          \"role\": \"admin\",\n          \"is_initial_admin\": true,\n          \"created_at\": \"2025-01-01T00:00:00Z\",\n          \"last_login_at\": \"2025-12-06T10:00:00Z\",\n          \"memory_count\": 150,\n          \"is_active\": true\n        }\n      ],\n      \"total\": 1\n    }","operationId":"list_system_admins_api_v1_admin_system_admins_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SystemAdminListResponse"}}}}}},"post":{"tags":["system-admins"],"summary":"Promote To System Admin","description":"Promote a user to system administrator.\n\nRequires: System Admin role\n\nArgs:\n    request: User ID to promote\n\nReturns:\n    Updated user with success message\n\nRaises:\n    404: User not found\n    400: User is already a system admin\n\nExample request:\n    {\n      \"user_id\": \"google-oauth2|123456\"\n    }\n\nExample response:\n    {\n      \"success\": true,\n      \"user\": { ... user details ... },\n      \"message\": \"User admin2@example.com promoted to system administrator\"\n    }","operationId":"promote_to_system_admin_api_v1_admin_system_admins_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteToSystemAdminRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PromoteToSystemAdminResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/system-admins/{user_id}":{"delete":{"tags":["system-admins"],"summary":"Demote System Admin","description":"Demote a system administrator to regular user.\n\nProtection:\n- Cannot demote initial admin (is_initial_admin=True)\n- Cannot demote last remaining admin\n\nRequires: System Admin role\n\nArgs:\n    user_id: User ID to demote\n\nReturns:\n    Success message\n\nRaises:\n    404: User not found\n    400: User is not a system admin\n    403: Demote operation is protected (initial admin or last admin)\n\nExample response:\n    {\n      \"success\": true,\n      \"message\": \"User admin2@example.com demoted to standard user\"\n    }","operationId":"demote_system_admin_api_v1_admin_system_admins__user_id__delete","parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/stats":{"get":{"tags":["workspace"],"summary":"Get Workspace Stats","description":"Get aggregated statistics across all user's contexts.\n\nReturns total memory count and per-context breakdown.\n\nArgs:\n    user: Authenticated user from session or API key\n    db: Database session\n\nReturns:\n    WorkspaceStatsResponse with aggregated stats\n\nExample:\n    GET /api/v1/workspace/stats\n    Response: {\n        \"total_memories\": 150,\n        \"context_count\": 3,\n        \"contexts\": [\n            {\"context_id\": \"...\", \"context_name\": \"default\", \"memory_count\": 100},\n            ...\n        ],\n        \"plan_name\": \"pro\"\n    }","operationId":"get_workspace_stats_api_v1_workspace_stats_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/usage/current":{"get":{"tags":["workspace"],"summary":"Get Workspace Usage Current","description":"Get workspace-wide usage vs plan limits.\n\nCounts are workspace-scoped (#1946): every live memory of the workspace,\nwhoever wrote it, and nothing from members' other workspaces.\n\nReturns:\n    Workspace usage statistics with plan limits and warning flags\n\nRaises:\n    HTTPException: 400 if no workspace selected, 500 on error","operationId":"get_workspace_usage_current_api_v1_workspace_usage_current_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageCurrentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/usage/history":{"get":{"tags":["workspace"],"summary":"Get Workspace Usage History","description":"Get workspace-wide historical usage data.\n\nAggregates daily API call counts across all workspace members.\n\nArgs:\n    days: Number of days to retrieve (default: 7, max: 90)\n\nReturns:\n    Historical usage data by day\n\nRaises:\n    HTTPException: 400 if no workspace selected, 500 on error","operationId":"get_workspace_usage_history_api_v1_workspace_usage_history_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":7,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/usage/breakdown":{"get":{"tags":["workspace"],"summary":"Get Workspace Usage Breakdown","description":"Get workspace-wide usage breakdown by endpoint.\n\nAggregates endpoint usage across all workspace members.\n\nArgs:\n    days: Number of days to analyze (default: 30, max: 90)\n\nReturns:\n    Usage breakdown by endpoint with percentages\n\nRaises:\n    HTTPException: 400 if no workspace selected, 500 on error","operationId":"get_workspace_usage_breakdown_api_v1_workspace_usage_breakdown_get","parameters":[{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageBreakdownResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/usage/members":{"get":{"tags":["workspace"],"summary":"Get Workspace Member Usage","description":"Get per-member usage breakdown for the current workspace.\n\nShows memory count and API calls per member.","operationId":"get_workspace_member_usage_api_v1_workspace_usage_members_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace/embedding-status":{"get":{"tags":["workspace"],"summary":"Get Embedding Status","description":"Get embedding processing queue status.\n\nIssue #93: Visibility into embedding pipeline status.\nReturns counts by status and details of the 50 most recently failed\nmemories, latest failure first; ``embedding_attempted_at`` is the failure time.","operationId":"get_embedding_status_api_v1_workspace_embedding_status_get","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by context ID (UUID)","title":"Context Id"},"description":"Filter by context ID (UUID)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces":{"get":{"tags":["workspaces"],"summary":"List Workspaces","description":"List all workspaces user belongs to.","operationId":"list_workspaces_api_v1_workspaces_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WorkspaceResponse"},"type":"array","title":"Response List Workspaces Api V1 Workspaces Get"}}}}}},"post":{"tags":["workspaces"],"summary":"Create Workspace","description":"Create a new workspace.\n\nThe authenticated user becomes the workspace owner.\n\nIssue #149: Plan tier enforcement - check multi-workspace restrictions.","operationId":"create_workspace_api_v1_workspaces_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceCreate"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}":{"get":{"tags":["workspaces"],"summary":"Get Workspace","description":"Get workspace details.","operationId":"get_workspace_api_v1_workspaces__workspace_id__get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["workspaces"],"summary":"Update Workspace","description":"Update workspace details (name, description).\n\nIssue #276: Slug removed.\n\nRequires owner role.","operationId":"update_workspace_api_v1_workspaces__workspace_id__put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["workspaces"],"summary":"Delete Workspace","description":"Delete workspace (soft delete).\n\nRequires owner role. Returns 409 ``BILLING-005`` while a paid subscription\nis still running for the workspace, including a scheduled cancellation\n(Issue #1940).","operationId":"delete_workspace_api_v1_workspaces__workspace_id__delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/switch":{"put":{"tags":["workspaces"],"summary":"Switch Workspace","description":"Switch to a different workspace.\n\nUpdates the user's current_workspace_id to the specified workspace.\nRequires the user to have at least viewer access to the workspace.","operationId":"switch_workspace_api_v1_workspaces__workspace_id__switch_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members":{"get":{"tags":["workspaces"],"summary":"List Members","description":"List all members of workspace.\n\nIssue #1164: session member+ (unchanged) OR workspace-owner API key.","operationId":"list_members_api_v1_workspaces__workspace_id__members_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceMemberResponse"},"title":"Response List Members Api V1 Workspaces  Workspace Id  Members Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["workspaces"],"summary":"Add Member","description":"Add a member to workspace.\n\nIssue #1164: session admin+ (unchanged) OR workspace-owner API key.\nProgrammatic principals cannot assign ``owner`` (422) — owner assignment\nstays with the ownership-transfer flow.","operationId":"add_member_api_v1_workspaces__workspace_id__members_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddMemberRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceMemberResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}":{"put":{"tags":["workspaces"],"summary":"Update Member Role","description":"Update member's role.\n\nIssue #1164: session admin+ (unchanged) OR workspace-owner API key.\nProgrammatic principals cannot assign ``owner`` (422).","operationId":"update_member_role_api_v1_workspaces__workspace_id__members__user_id__put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberRoleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceMemberResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["workspaces"],"summary":"Remove Member","description":"Remove member from workspace.\n\nIssue #217: owner role only. Cannot remove owner.\nIssue #1164: session owner (unchanged) OR workspace-owner API key.","operationId":"remove_member_api_v1_workspaces__workspace_id__members__user_id__delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/context-access":{"put":{"tags":["workspaces"],"summary":"Update Member Context Access","description":"Update member's allowed context access.\n\nIssue #234: Context access restriction for member/viewer.\n\n- allowed_context_ids=null: No restriction (can access all contexts)\n- allowed_context_ids=[]: No context access\n- allowed_context_ids=[uuid1, uuid2]: Only these contexts accessible\n\nNote: This setting only applies to member/viewer roles.\nowner/admin roles always have full access (this setting is ignored).\n\nRequires admin or owner role.","operationId":"update_member_context_access_api_v1_workspaces__workspace_id__members__user_id__context_access_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMemberContextAccessRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/stats":{"get":{"tags":["workspaces"],"summary":"Get Workspace Stats","description":"Get workspace statistics.\n\nReturns:\n    - total_memories: Total memories across all contexts\n    - context_count: Number of contexts\n    - member_count: Number of members","operationId":"get_workspace_stats_api_v1_workspaces__workspace_id__stats_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/contexts/stats":{"get":{"tags":["workspaces"],"summary":"Get Context Stats","description":"Get per-context usage statistics.\n\nIssue #249: Context usage overview for workspace page.\n\nReturns context-level statistics including memory count, storage,\nlast activity, and member count for each context.\n\nSecurity:\n    - Requires workspace Owner or Admin role\n    - Only shows contexts user has access to (RBAC)\n\nReturns:\n    WorkspaceContextStatsResponse with per-context stats and totals","operationId":"get_context_stats_api_v1_workspaces__workspace_id__contexts_stats_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceContextStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/contexts/{context_id}/usage-timeline":{"get":{"tags":["workspaces"],"summary":"Get Context Usage Timeline","description":"Get time-series usage statistics for a specific context.\n\nIssue #249: Context activity timeline for graphs.\n\nArgs:\n    workspace_id: Workspace ID\n    context_id: Context ID\n    days: Number of days to include (default: 7, max: 30)\n\nReturns:\n    Daily API call counts and unique users","operationId":"get_context_usage_timeline_api_v1_workspaces__workspace_id__contexts__context_id__usage_timeline_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextUsageTimelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/contexts/{context_id}/user-activity":{"get":{"tags":["workspaces"],"summary":"Get Context User Activity","description":"Get per-user activity statistics for a specific context.\n\nIssue #249: User activity breakdown for context.\n\nArgs:\n    workspace_id: Workspace ID\n    context_id: Context ID\n    days: Number of days to include (default: 7, max: 30)\n\nReturns:\n    Per-user API call counts and last activity","operationId":"get_context_user_activity_api_v1_workspaces__workspace_id__contexts__context_id__user_activity_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextUserActivityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/memory-timeline":{"get":{"tags":["workspaces"],"summary":"Get Workspace Memory Timeline","description":"Get workspace memory creation timeline.\n\nIssue #275 Task 6: Memory count timeline visualization.\nIssue #134: Optional context_id filter for dashboard global filter.","operationId":"get_workspace_memory_timeline_api_v1_workspaces__workspace_id__memory_timeline_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryTimelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/contexts/{context_id}/public-api-stats":{"get":{"tags":["workspaces"],"summary":"Get Context Public Api Stats","description":"Get Public API usage statistics for a public context.\n\nIssue #265: Resource Ingest API and Public Search API stats.\n\nArgs:\n    workspace_id: Workspace ID\n    context_id: Context ID\n    days: Number of days (default: 7, max: 30)\n\nReturns:\n    PublicAPIStatsResponse with resource_ingest and public_search stats\n\nSecurity:\n    - Requires Workspace Member or higher\n    - Context must be public (is_public=true)\n\nExample:\n    GET /api/v1/workspaces/{workspace_id}/contexts/{context_id}/public-api-stats?days=7","operationId":"get_context_public_api_stats_api_v1_workspaces__workspace_id__contexts__context_id__public_api_stats_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":7,"title":"Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicAPIStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/openai-key-status":{"get":{"tags":["workspaces"],"summary":"Check Openai Key Status","description":"Check if workspace has valid OpenAI API key.\n\nIssue #181: OpenAI API key guidance in context creation.\n\nReturns status about whether workspace has an enabled OpenAI API key,\nand whether current user has permissions to configure it.\n\nArgs:\n    workspace_id: Workspace ID\n    request: HTTP request\n    db: Database session\n\nReturns:\n    OpenAIKeyStatusResponse with has_key, can_configure, external_keys_url\n\nRaises:\n    HTTPException 403: User not member of workspace","operationId":"check_openai_key_status_api_v1_workspaces__workspace_id__openai_key_status_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OpenAIKeyStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/transfer-ownership":{"post":{"tags":["workspaces"],"summary":"Transfer Workspace Ownership","description":"Transfer ownership of a workspace to an existing member.\n\nCurrent-owner-only and session-only (a Bearer API key / OAuth token is\nrejected 403 — a sensitive governance action must require a real browser\nsession). The owner check binds to the **path** ``workspace_id``, never the\ncaller's current workspace. Atomic single-owner invariant under a row lock,\nidempotent, and writes an audit row + bumps the ownership epoch on success.","operationId":"transfer_workspace_ownership_api_v1_workspaces__workspace_id__transfer_ownership_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferOwnershipRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransferOwnershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/invitations":{"post":{"tags":["invitations"],"summary":"Create workspace invitation","description":"Create an invitation to join the workspace. Requires owner or admin role.","operationId":"create_invitation_api_v1_workspaces__workspace_id__invitations_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceInvitationCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceInvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["invitations"],"summary":"List workspace invitations","description":"List all invitations for the workspace. Requires owner or admin role.","operationId":"list_invitations_api_v1_workspaces__workspace_id__invitations_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"include_accepted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Accepted"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceInvitationResponse"},"title":"Response List Invitations Api V1 Workspaces  Workspace Id  Invitations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/invitations/{invitation_id}":{"delete":{"tags":["invitations"],"summary":"Delete workspace invitation","description":"Delete (revoke) an invitation. Requires owner or admin role.","operationId":"delete_invitation_api_v1_workspaces__workspace_id__invitations__invitation_id__delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"invitation_id","in":"path","required":true,"schema":{"type":"string","pattern":"^winv_[0-9A-Za-z]{22}$","description":"Public id of the workspace invitation (`winv_` + 22 base62 characters).","examples":["winv_0000000000000000000000"],"title":"Invitation Id"},"description":"Public id of the workspace invitation (`winv_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Invitation Api V1 Workspaces  Workspace Id  Invitations  Invitation Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invitations/accept":{"post":{"tags":["invitations"],"summary":"Accept workspace invitation","description":"Accept an invitation to join an workspace. Public endpoint (authenticated users only).","operationId":"accept_invitation_api_v1_invitations_accept_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitationRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AcceptInvitationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/invitations/pending":{"get":{"tags":["invitations"],"summary":"Get pending invitations for current user","description":"Get all pending (non-expired) invitations for the authenticated user's email.","operationId":"get_pending_invitations_api_v1_invitations_pending_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PendingInvitationsResponse"}}}}}}},"/api/v1/invitations/{token}":{"get":{"tags":["invitations"],"summary":"Get invitation info","description":"Get invitation information by token. Does not require authentication (for preview before login).","operationId":"get_invitation_info_api_v1_invitations__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Invitation Info Api V1 Invitations  Token  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/member-quota":{"get":{"tags":["invitations"],"summary":"Get member quota status","description":"Get current member count and limit for workspace. Requires member access.","operationId":"get_member_quota_api_v1_workspaces__workspace_id__member_quota_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Member Quota Api V1 Workspaces  Workspace Id  Member Quota Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/plan":{"get":{"tags":["workspace-plan"],"summary":"Get Workspace Plan","description":"Get workspace plan information.\n\nIssue #246: Owner-only access (frontend shows in owner-only menu).\n\nReturns current plan, usage stats, quotas, and upgrade/downgrade options.","operationId":"get_workspace_plan_api_v1_workspaces__workspace_id__plan_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspacePlanInfo"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/plans/available":{"get":{"tags":["workspace-plan"],"summary":"Get Available Plans","description":"Get available plan tiers.\n\nPublic information, accessible by any authenticated user.","operationId":"get_available_plans_api_v1_workspaces_plans_available_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailablePlanInfo"},"title":"Response Get Available Plans Api V1 Workspaces Plans Available Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/plans/tiers":{"get":{"tags":["workspace-plan"],"summary":"Get Plan Tier Matrix","description":"Curated per-tier feature matrix for the Plan page comparison (#1138).\n\nSingle source of truth = ``config/plan_tiers.py`` (env-overridable). Price is\nomitted by design — pricing lives in www / the payment service. Returns the\ntiers in upgrade order (free → basic → pro). Public reference data; accessible\nby any authenticated session user (the Plan page itself is owner-gated).\n\nEach row also carries ``feature_enforcement`` (#1648): the registry's\nper-feature runtime mode, so a client can tell a real gate from a plan-page\nlabel instead of assuming every feature name is enforced.\n\nPath note: the ``/workspaces/plans/...`` two-segment shape (mirroring\n``/workspaces/plans/available``) is deliberate — a one-segment\n``/workspaces/plan-tiers`` would be shadowed by the earlier-registered\n``GET /workspaces/{workspace_id}`` route and 422 on the UUID parse.","operationId":"get_plan_tier_matrix_api_v1_workspaces_plans_tiers_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/PlanTierFeature"},"title":"Response Get Plan Tier Matrix Api V1 Workspaces Plans Tiers Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials":{"get":{"tags":["member-credentials"],"summary":"Get Member Credentials","description":"Get or create member credentials (Lazy initialization).\n\nMigration 034: Zero-knowledge model (unchanged for sessions).\n- A session caller sees plaintext ONLY for their OWN key while it is still\n  visible (``get_or_create_credentials`` includes ``plaintext_key`` only\n  when ``requester_id == user_id``). Session owner/admin viewing ANOTHER\n  member sees metadata only.\n- Issue #1165: an API-key OWNER principal may also view another member's key,\n  but the response is ALWAYS metadata-only (``plaintext_key`` nulled) —\n  programmatic responses get logged, so plaintext must never appear.\n- OAuth bearer principals are rejected (403).\n\nRaises:\n    HTTPException: If not authorized.","operationId":"get_member_credentials_api_v1_workspaces__workspace_id__members__user_id__credentials_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberCredentialsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/api-key/hide":{"post":{"tags":["member-credentials"],"summary":"Hide Api Key","description":"Manually hide API key (Owner only).\n\nMigration 034: Zero-knowledge - only owner can hide.\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Status message\n\nRaises:\n    HTTPException: If not owner or key not found","operationId":"hide_api_key_api_v1_workspaces__workspace_id__members__user_id__credentials_api_key_hide_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Hide Api Key Api V1 Workspaces  Workspace Id  Members  User Id  Credentials Api Key Hide Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/api-key/regenerate":{"post":{"tags":["member-credentials"],"summary":"Regenerate Api Key","description":"Regenerate API key (Permission-based).\n\nMigration 034: Creates new key, revokes old one.\n\nNew permission hierarchy:\n- Self: Always allowed\n- Owner: Can regenerate admin/member/viewer's keys\n- Admin: Can regenerate member/viewer's keys\n\nThe key owner is emailed a security notice; when an admin acted, it\nnames the admin (#1752).\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    request: The request (IP / user agent for the notice)\n    background_tasks: Runs the notice after the response\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    New plaintext key (shown once)\n\nRaises:\n    HTTPException: If not authorized or key not found","operationId":"regenerate_api_key_api_v1_workspaces__workspace_id__members__user_id__credentials_api_key_regenerate_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateAPIKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/api-keys":{"post":{"tags":["member-credentials"],"summary":"Create Api Key","description":"Create new API key.\n\nIssue #1165: two principals.\n- **Session** (web UI): self-mint only (``#252`` unchanged) — the caller\n  may only mint their OWN key. ``expires_days`` is NOT accepted on this path\n  (400 if supplied — session keys keep their historical no-expiry behavior);\n  ``bound_context_id`` is still allowed (#626 public-bound path).\n- **API-key owner** (programmatic): may mint for ANOTHER member, gated to\n  workspace-OWNER on the path workspace. Guardrails: 403 when target ==\n  caller (anti self-replication, #252 threat); 403 unless the target's\n  workspace role is ``member``/``viewer`` (privilege-downgrade provisioning\n  only); ``expires_days`` REQUIRED (400 if omitted — never-expiring CI keys\n  are not an acceptable default); ``bound_context_id`` rejected (400 —\n  public-bound keys stay self-only); the minted key is force-hidden so the\n  plaintext exists only in this one 201 response.\nOAuth bearer principals are rejected (403).\n\nIssue #626: If ``data.bound_context_id`` is supplied, the key is stored\nas a public-bound key (``api_keys.workspace_id`` is left NULL) and is\nattributed to that one ``is_public=true`` context for the rate-limit /\naudit / revoke surface on ``/api/v1/public/{ctx}/*``. The binding is\nimmutable — to change it, revoke this key and create a new one.\n\nIssue #1752: the key owner is emailed a security notice after the commit\n(naming the owner key's user on the owner-provisioned path).\n\nArgs:\n    workspace_id: Workspace ID (from URL — used as permission scope; also\n        assigned to ``api_keys.workspace_id`` unless a bound context is set)\n    user_id: Target user ID\n    data: API key creation data (name, auto_hide_minutes, bound_context_id)\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Created API key with plaintext (shown once)\n\nRaises:\n    HTTPException: If not owner, name already exists, tier gate fails,\n        or the bound context is missing / not public / not in this workspace.","operationId":"create_api_key_api_v1_workspaces__workspace_id__members__user_id__credentials_api_keys_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAPIKeyRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemberAPIKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/api-key":{"delete":{"tags":["member-credentials"],"summary":"Delete Api Key","description":"Delete API key (Permission-based).\n\nPermission matrix:\n- Owner: Delete own key\n- Workspace Owner: Delete any member's key\n- Workspace Admin: Delete member/viewer's key\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Status message\n\nRaises:\n    HTTPException: If not authorized or key not found","operationId":"delete_api_key_api_v1_workspaces__workspace_id__members__user_id__credentials_api_key_delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Api Key Api V1 Workspaces  Workspace Id  Members  User Id  Credentials Api Key Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/api-keys/{key_id}":{"delete":{"tags":["member-credentials"],"summary":"Delete Api Key By Id","description":"Delete/revoke a specific API key by its public id (``akey_...``, #1008).\n\nIssue #626: per-id endpoint (also revokes public-bound keys). The URL\n``workspace_id`` is the permission scope, not a filter on the key's column.\n\nIssue #1165: two principals.\n- **Session**: self-only (``#252`` unchanged) — hard delete of your own key.\n- **API-key OWNER** (programmatic): may revoke ANOTHER member's key (target\n  must be member/viewer). This is a **soft revoke** (``revoked_at=now``, row\n  retained for forensics), and the audit row is written BEFORE the state\n  change. OAuth bearer principals are rejected (403).\n\nRaises:\n    HTTPException: 403 if not permitted, 404 if key not found.","operationId":"delete_api_key_by_id_api_v1_workspaces__workspace_id__members__user_id__credentials_api_keys__key_id__delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"key_id","in":"path","required":true,"schema":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","description":"Public id of the API key (`akey_` + 22 base62 characters).","examples":["akey_0000000000000000000000"],"title":"Key Id"},"description":"Public id of the API key (`akey_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Api Key By Id Api V1 Workspaces  Workspace Id  Members  User Id  Credentials Api Keys  Key Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/oauth/hide":{"post":{"tags":["member-credentials"],"summary":"Hide Oauth App","description":"Manually hide OAuth app secret (Owner only).\n\nMigration 034: Zero-knowledge - only owner can hide.\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Status message\n\nRaises:\n    HTTPException: If not owner or app not found","operationId":"hide_oauth_app_api_v1_workspaces__workspace_id__members__user_id__credentials_oauth_hide_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Hide Oauth App Api V1 Workspaces  Workspace Id  Members  User Id  Credentials Oauth Hide Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/oauth/regenerate":{"post":{"tags":["member-credentials"],"summary":"Regenerate Oauth Secret","description":"Regenerate OAuth client secret (Permission-based).\n\nMigration 034: Generates new secret, updates hash.\n\nNew permission hierarchy:\n- Self: Always allowed\n- Owner: Can regenerate admin/member/viewer's secrets\n- Admin: Can regenerate member/viewer's secrets\n\nThe app owner is emailed a security notice; when an admin acted, it\nnames the admin (#1752).\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    request: The request (IP / user agent for the notice)\n    background_tasks: Runs the notice after the response\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    New plaintext secret (shown once)\n\nRaises:\n    HTTPException: If not authorized or app not found","operationId":"regenerate_oauth_secret_api_v1_workspaces__workspace_id__members__user_id__credentials_oauth_regenerate_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegenerateOAuthSecretResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/members/{user_id}/credentials/oauth":{"delete":{"tags":["member-credentials"],"summary":"Delete Oauth App","description":"Delete OAuth app (Permission-based).\n\nPermission matrix:\n- Owner: Delete own app\n- Workspace Owner: Delete any member's app\n- Workspace Admin: Delete member/viewer's app\n\nArgs:\n    workspace_id: Workspace ID\n    user_id: Target user ID\n    user: Current user (from auth)\n    db: Database session\n\nReturns:\n    Status message\n\nRaises:\n    HTTPException: If not authorized or app not found","operationId":"delete_oauth_app_api_v1_workspaces__workspace_id__members__user_id__credentials_oauth_delete","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Delete Oauth App Api V1 Workspaces  Workspace Id  Members  User Id  Credentials Oauth Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/neural-config":{"get":{"tags":["neural-config"],"summary":"List Neural Config","description":"List all neural config parameters.\n\nAdmin-only endpoint.\n\nArgs:\n    admin: Authenticated admin user\n    db: Database session\n    category: Optional filter by category\n\nReturns:\n    List of neural config items grouped by category","operationId":"list_neural_config_api_v1_admin_neural_config_get","parameters":[{"name":"category","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Category"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeuralConfigListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/neural-config/{key}":{"get":{"tags":["neural-config"],"summary":"Get Neural Config","description":"Get a specific neural config parameter.\n\nAdmin-only endpoint.","operationId":"get_neural_config_api_v1_admin_neural_config__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/NeuralConfigItem"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["neural-config"],"summary":"Update Neural Config","description":"Update a neural config parameter.\n\nAdmin-only endpoint. Validates value against min/max constraints.","operationId":"update_neural_config_api_v1_admin_neural_config__key__put","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeuralConfigUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeuralConfigUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/neural-config/reset":{"post":{"tags":["neural-config"],"summary":"Reset Neural Config","description":"Reset all neural config parameters to default values.\n\nAdmin-only endpoint.","operationId":"reset_neural_config_api_v1_admin_neural_config_reset_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NeuralConfigResetResponse"}}}}}}},"/api/v1/admin/sleep-reports":{"get":{"tags":["sleep-reports"],"summary":"List Sleep Maintenance reports (admin)","description":"List Sleep Maintenance reports with filters and pagination.\n\nAdmin-only. Filterable by status, context_id, user_id.\nSorted by started_at DESC.","operationId":"list_sleep_reports_api_v1_admin_sleep_reports_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepReportListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/sleep-reports/{report_id}":{"get":{"tags":["sleep-reports"],"summary":"Get a single Sleep Maintenance report (admin)","description":"Get a single Sleep Maintenance report with its full action audit log.\n\nAdmin-only.","operationId":"get_sleep_report_detail_api_v1_admin_sleep_reports__report_id__get","parameters":[{"name":"report_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepReportDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/sleep-reports":{"get":{"tags":["workspaces"],"summary":"List Sleep Maintenance reports scoped to one workspace","description":"List Sleep Maintenance reports for a single workspace.\n\nRequires workspace **owner** or **admin** role.\n\nCross-workspace probing is impossible here: the path-bound\n``workspace_id`` is the only scope passed to the service, and\n``check_workspace_admin`` rejects callers without admin/owner\nmembership in *that specific* workspace before the query runs.","operationId":"workspace_list_sleep_reports_api_v1_workspaces__workspace_id__sleep_reports_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepReportListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/sleep-reports/{report_id}":{"get":{"tags":["workspaces"],"summary":"Get a single Sleep Maintenance report scoped to one workspace","description":"Get a single Sleep Maintenance report with its full action audit log.\n\nRequires workspace **owner** or **admin** role.\n\nReturns 404 (not 403) when the report exists but belongs to a\ndifferent workspace — uniform disclosure policy per #389 / CWE-639.","operationId":"workspace_get_sleep_report_detail_api_v1_workspaces__workspace_id__sleep_reports__report_id__get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"report_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Report Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepReportDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/cost-aggregation":{"get":{"tags":["admin"],"summary":"Cost aggregation across all workspaces (admin)","description":"Aggregate LLM + embedding cost across every workspace.\n\nAdmin-only (``require_admin``). ``workspace_id`` here is a\nconvenience filter — admins can scope to one workspace without\nusing the workspace-scoped route — but absent it, every workspace\nis included.","operationId":"admin_cost_aggregation_api_v1_admin_cost_aggregation_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"Aggregation period: day | week | month","default":"day","title":"Period"},"description":"Aggregation period: day | week | month"},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inclusive lower-bound date","title":"From"},"description":"Inclusive lower-bound date"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inclusive upper-bound date","title":"To"},"description":"Inclusive upper-bound date"},{"name":"workspace_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Filter to a single workspace","title":"Workspace Id"},"description":"Filter to a single workspace"},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to a single user","title":"User Id"},"description":"Filter to a single user"},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by source: ['sleep', 'analysis', 'supersede_verify', 'llm_selector']","title":"Source"},"description":"Filter by source: ['sleep', 'analysis', 'supersede_verify', 'llm_selector']"},{"name":"paid_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by billing classification: ['platform', 'byok']","title":"Paid By"},"description":"Filter by billing classification: ['platform', 'byok']"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostAggregationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspaces/{workspace_id}/cost-aggregation":{"get":{"tags":["workspaces"],"summary":"Cost aggregation scoped to one workspace (owner/admin)","description":"Aggregate cost for a single workspace.\n\nRequires workspace **owner** or **admin** role — viewer / member\nare rejected because cost aggregates leak per-user activity volume\nacross private contexts that those roles should not see. Owners\nreview billing; admins manage workspace settings.\n\nCross-workspace probing is impossible here: the path-bound\n``workspace_id`` is the only scope passed to the service, and\n``check_workspace_admin`` rejects callers without admin/owner\nmembership in *that specific* workspace before the query runs.","operationId":"workspace_cost_aggregation_api_v1_workspaces__workspace_id__cost_aggregation_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Workspace Id"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"Aggregation period: day | week | month","default":"day","title":"Period"},"description":"Aggregation period: day | week | month"},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inclusive lower-bound date","title":"From"},"description":"Inclusive lower-bound date"},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Inclusive upper-bound date","title":"To"},"description":"Inclusive upper-bound date"},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to a single user","title":"User Id"},"description":"Filter to a single user"},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by source: ['sleep', 'analysis', 'supersede_verify', 'llm_selector']","title":"Source"},"description":"Filter by source: ['sleep', 'analysis', 'supersede_verify', 'llm_selector']"},{"name":"paid_by","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by billing classification: ['platform', 'byok']","title":"Paid By"},"description":"Filter by billing classification: ['platform', 'byok']"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CostAggregationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses/preview":{"post":{"tags":["analyses"],"summary":"Estimate analysis cost without starting a run","description":"Returns the cost estimate for a hypothetical run.\n\nGoes through the full 4-gate chain so a non-Pro / quota-exhausted\n/ non-allowlisted user gets the same 403/429 they would on POST,\nrather than seeing the price and then being blocked at confirm.","operationId":"preview_analysis_api_v1_contexts__context_id__analyses_preview_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisPreviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses":{"post":{"tags":["analyses"],"summary":"Start a Memory Analysis run","description":"Kick off a background analysis run.\n\nReturns 202 with the new run_id. The pipeline runs in\n``asyncio.create_task(run_analysis_task)``; clients poll\n``GET /analyses/{run_id}`` for status transitions.\n\nError mapping (delegated to global handler via custom exceptions):\n\n- 422 ValidationError — context exceeds the #1244 run-size cap\n  (checked BEFORE the idempotency guard, so an over-cap context\n  with a run already in flight gets 422, not the 409+run_id hint;\n  cancel/poll that run via the list endpoint).\n- 409 ConflictError  — a prior run is still ``running`` for the\n  same (workspace, context). Body carries the existing run_id.\n- 422 ValidationError — BYOK key missing (orchestrator's\n  pre-flight) OR caller-supplied ``model_id`` does not exist.","operationId":"start_analysis_api_v1_contexts__context_id__analyses_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisStartRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisStartResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["analyses"],"summary":"List analysis runs for a context","description":"Return runs for the context, sorted by ``started_at DESC``.","operationId":"list_runs_api_v1_contexts__context_id__analyses_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque pagination cursor","title":"Cursor"},"description":"Opaque pagination cursor"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses/active":{"get":{"tags":["analyses"],"summary":"Most recent succeeded run for a context","description":"Returns 404 when the context has no succeeded run yet.","operationId":"get_active_api_v1_contexts__context_id__analyses_active_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses/{run_id}":{"get":{"tags":["analyses"],"summary":"Fetch one analysis run by id","operationId":"get_run_api_v1_contexts__context_id__analyses__run_id__get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisRow"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["analyses"],"summary":"Soft-cancel a running analysis (status → cancelled)","description":"Mark a running run as ``cancelled``.\n\nNo-op (idempotent 200) when the run is already in a terminal\nstate — not all clients will ship a \"stop polling\" hook to detect\nthat the run finished between their last poll and the cancel\nclick. 409 only for the genuinely contradictory case (status not\nin {running, succeeded, failed, cancelled}).\n\nCancellation does NOT decrement the daily quota — the run row\nhas already been INSERTed by orchestrator.start, and BYOK /\npartial LLM cost may have been incurred (#496 quota AC).","operationId":"cancel_run_api_v1_contexts__context_id__analyses__run_id__delete","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AnalysisCancelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses/{run_id}/clusters":{"get":{"tags":["analyses"],"summary":"List all clusters for an analysis run","description":"Returns every cluster row for the run, ordered by ``cluster_index``.\n\nPowers the #497 frontend cluster list, scatter centroids, and the\nper-cluster property-stats panel. Cluster count is bounded\nserver-side by ``ceil(sqrt(memory_count))`` (≈ 90 on an 8000-memory\nrun), so the endpoint returns the full set with no pagination — the\nfrontend would otherwise have to make up-to-N round-trips to render\nthe cluster list which dominates the latency budget on this page.\n\nA run that is still ``running`` or that ``failed`` before the\nlabeler stage may have zero cluster rows; the response is then\n``items: []`` rather than 404 (matches the running-status semantics\nof the run-level read).","operationId":"list_run_clusters_api_v1_contexts__context_id__analyses__run_id__clusters_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/analyses/{run_id}/positions":{"get":{"tags":["analyses"],"summary":"List per-memory 2D positions for an analysis run","description":"Returns every ``(memory_id, x, y, cluster_index)`` for the scatter dots.\n\nBounded by ``MemoryAnalysis.input_count`` (capped at 10k memories\nin the orchestrator). Returns ~640 KB worst-case JSON which is\nacceptable on an explicit page open. No pagination — splitting\nforces the frontend to coordinate progressive renders for very\nlittle user-visible benefit, and the tab is gated by 4-stage\nauth so the audience is small.\n\nSame context-boundary check as ``/clusters``: the URL's\n``context_id`` must match the run's stored ``context_id``.","operationId":"list_run_positions_api_v1_contexts__context_id__analyses__run_id__positions_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"run_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Run Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PositionListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/sleep/run":{"post":{"tags":["admin-sleep"],"summary":"Trigger Sleep Maintenance (admin manual run)","description":"Manually trigger a Sleep Maintenance run for the calling admin.\n\nSelf-scoped: the run targets only contexts the admin created (matched\non ``Context.created_by``), excluding soft-deleted contexts and\ncontexts with ``sleep_mode='skip'``. When ``context_id`` is provided a\nsingle run is scheduled for that context; otherwise one run is\nscheduled per eligible context.\n\nReturns:\n    202 with ``{\"report_ids\": [...]}``. Phase execution happens in the\n    background; poll ``GET /admin/sleep-reports/{id}`` for progress.\n\nRaises:\n    409 ``SLEEP-002`` if this user already has a sleep run\n    with ``status='running'`` — the response body includes\n    ``running_report_id`` and ``started_at`` so the UI can link to it.\n    404 ``SLEEP-003`` if no eligible contexts match.\n\nPhase 1 limitation:\n    The 409 guard is best-effort under concurrent requests. Two\n    simultaneous POSTs could each pass the check before either commits,\n    resulting in overlapping batches. Acceptable for single-admin\n    dogfooding; Phase 2 will enforce exactly-once via a unique partial\n    index on ``sleep_reports(user_id) WHERE status='running'``.","operationId":"trigger_sleep_run_api_v1_admin_sleep_run_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepRunRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SleepRunResponse"}}}},"409":{"description":"A sleep run is already in progress for this user.","content":{"application/json":{"example":{"error":"SLEEP-002","message":"A sleep run is already in progress for this user.","details":{"running_report_id":"00000000-0000-0000-0000-000000000000","started_at":"2026-04-09T12:00:00Z"}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/sleep/actions/{action_id}/undo-merge":{"post":{"tags":["admin-sleep"],"summary":"Undo one dedup merge (restore the merged-away memory)","description":"Restore the loser of ONE dedup merge — row and Qdrant vector (#1209).\n\nSelf-scoped like the manual sleep trigger: the merge's report must belong\nto the calling admin. The undo is itself audited as an ``undo_merge``\naction on the same sleep report, so the merge's full history reads out of\none audit log. Reversibility is bounded by the declared retention window;\na purged loser returns 410 with the setting named.","operationId":"undo_merge_api_v1_admin_sleep_actions__action_id__undo_merge_post","parameters":[{"name":"action_id","in":"path","required":true,"schema":{"type":"integer","title":"Action Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UndoMergeResponse"}}}},"400":{"description":"The action is not a dedup merge."},"404":{"description":"Action not found or not owned by the caller."},"409":{"description":"Conflicts with current state. ``SLEEP-UNDO-already_restored``: the memory or shadow edge is already back in its post-undo state (no-op). ``SLEEP-UNDO-not_merge_deleted``: the memory was deleted by something other than this merge. ``SLEEP-UNDO-edge_changed`` (#1450): a later writer changed or removed the shadow edge, so the pre-merge state could not be restored — unlike the other two, the undo did NOT happen."},"410":{"description":"The merge loser was purged by the Sleep retention window (sleep_merge_retention_days) or the platform cleanup sweep (CLEANUP_DELETED_MEMORIES_RETENTION_DAYS, default 30 days) — no longer restorable."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/memory-health":{"get":{"tags":["admin-memory-health"],"summary":"Per-context memory-health report (self-diagnosis)","description":"Build the thresholded health document(s) for the calling admin.\n\nSections: consolidation (judge health, merge audit, soft-delete\nbacklog), graph (edge composition, weight invariant, cold-graph\ncheck), retrieval (usage volume, config posture). Label-free signals\nonly — gold-label rates (stale_only, P@k) live in the #1210 eval\ngates, not here.","operationId":"get_memory_health_api_v1_admin_memory_health_get","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"const":"unattributed","type":"string"},{"type":"null"}],"description":"Omit for the per-context breakdown. Pass a context UUID for that context's 3-section detail (must be owned by the caller — otherwise 404), or the sentinel 'unattributed' for signals recorded without a context.","title":"Context Id"},"description":"Omit for the per-context breakdown. Pass a context UUID for that context's 3-section detail (must be owned by the caller — otherwise 404), or the sentinel 'unattributed' for signals recorded without a context."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MemoryHealthBreakdownResponse"},{"$ref":"#/components/schemas/MemoryHealthDetailResponse"}],"title":"Response Get Memory Health Api V1 Admin Memory Health Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/neural/recalibrate":{"post":{"tags":["admin-neural"],"summary":"Recalibrate","description":"Enqueue a deduped model-global calibration job.\n\nThe ``AdminUser`` dependency rejects non-admins with 403 before this\nhandler runs. ``model`` is length-bounded and ``dimensions`` is\nrange-bounded by FastAPI validation. No rate limiting is applied at\nthis layer — the Redis dedup lock is the single source of truth for\n\"one job per (model, dims) in flight at a time\", and admins can\nself-serve retries without hammering the compute.","operationId":"recalibrate_api_v1_admin_neural_recalibrate_post","parameters":[{"name":"model","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":100,"title":"Model"}},{"name":"dimensions","in":"query","required":true,"schema":{"type":"integer","maximum":65536,"minimum":1,"title":"Dimensions"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecalibrateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/signup-gate/config":{"get":{"tags":["admin-signup-gate"],"summary":"Get Config","operationId":"get_config_api_v1_admin_signup_gate_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupGateConfigResponse"}}}}}},"put":{"tags":["admin-signup-gate"],"summary":"Update Config","operationId":"update_config_api_v1_admin_signup_gate_config_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupGateConfigUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignupGateConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/signup-gate/allowlist":{"get":{"tags":["admin-signup-gate"],"summary":"List Allowlist","operationId":"list_allowlist_api_v1_admin_signup_gate_allowlist_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/AllowlistEntryResponse"},"type":"array","title":"Response List Allowlist Api V1 Admin Signup Gate Allowlist Get"}}}}}},"post":{"tags":["admin-signup-gate"],"summary":"Add To Allowlist","description":"Add a GitHub or Google user to the manual allowlist (#655).\n\nProvider is set explicitly via ``payload.provider`` (defaulting to\n``\"github\"`` for pre-#655 callers). The required field per provider:\n\n* ``provider=\"github\"`` → ``github_username`` (resolved to numeric ID\n  via the GitHub API).\n* ``provider=\"google\"`` → ``email`` (resolved to OIDC ``sub`` via the\n  local ``users`` table; the user must have OAuth'd at least once).","operationId":"add_to_allowlist_api_v1_admin_signup_gate_allowlist_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowlistAddRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AllowlistEntryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/signup-gate/allowlist/{entry_id}":{"delete":{"tags":["admin-signup-gate"],"summary":"Remove From Allowlist","operationId":"remove_from_allowlist_api_v1_admin_signup_gate_allowlist__entry_id__delete","parameters":[{"name":"entry_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Entry Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/beta-invites/me":{"get":{"tags":["beta-invites"],"summary":"Get My Beta Invites","description":"Return the caller's invite quota standing and their invites, newest first.\n\nArgs:\n    user: Authenticated session user.\n    db: Database session.\n\nReturns:\n    ``quota`` / ``used`` (= ``active`` + ``redeemed``) / ``remaining`` and the\n    per-invite lifecycle, label and — for redeemed invites — admitted e-mail.","operationId":"get_my_beta_invites_api_v1_beta_invites_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaInviteSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/beta-invites":{"post":{"tags":["beta-invites"],"summary":"Create Beta Invite","description":"Mint a one-time invite link. The URL is returned here and never again.\n\nArgs:\n    request: FastAPI request (IP / User-Agent for the audit row).\n    user: Authenticated session user (the inviter).\n    body: Optional ``{\"label\": ...}`` (#1595). Clients that predate it send\n        no body at all, which keeps working.\n    db: Database session.\n\nReturns:\n    The invite id, its plaintext URL, its expiry and its label.\n\nRaises:\n    BetaInviteQuotaExceededError: 409 ``BETA-INVITE-001`` at the cap.","operationId":"create_beta_invite_api_v1_beta_invites_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/BetaInviteCreateRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaInviteCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/beta-invites/{invite_id}/reissue":{"post":{"tags":["beta-invites"],"summary":"Reissue Beta Invite","description":"Replace an own ``active`` or ``expired`` invite with a fresh link (#1595).\n\nFor a recipient who lost the link: only the hash is stored, so the URL cannot\nbe shown again. The old invite is revoked and a new one carrying the same\nlabel is minted in one transaction; the quota standing of an ``active``\ninvite is unchanged. Takes no body.\n\nArgs:\n    invite_id: The invite to replace.\n    request: FastAPI request (IP / User-Agent for the audit rows).\n    user: Authenticated session user.\n    db: Database session.\n\nReturns:\n    Exactly the create payload — the new invite and its plaintext URL, once.\n\nRaises:\n    NotFoundException: 404 — unknown id, or someone else's invite.\n    BetaInviteAlreadyRedeemedError: 409 ``BETA-INVITE-002``.\n    BetaInviteAlreadyRevokedError: 409 ``BETA-INVITE-003`` — also what the\n        second request of a double-click gets; it mints nothing.\n    BetaInviteQuotaExceededError: 409 ``BETA-INVITE-001`` — an ``expired``\n        invite holds no slot, so reissuing one at the cap is refused (and the\n        invite is left as it was).","operationId":"reissue_beta_invite_api_v1_beta_invites__invite_id__reissue_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaInviteCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/beta-invites/{invite_id}":{"delete":{"tags":["beta-invites"],"summary":"Revoke Beta Invite","description":"Revoke one of the caller's own unused invites (frees its quota slot).\n\nArgs:\n    invite_id: The invite to revoke.\n    request: FastAPI request (IP / User-Agent for the audit row).\n    user: Authenticated session user.\n    db: Database session.\n\nReturns:\n    204 No Content.\n\nRaises:\n    NotFoundException: 404 — unknown id, or someone else's invite.\n    BetaInviteAlreadyRedeemedError: 409 ``BETA-INVITE-002``.","operationId":"revoke_beta_invite_api_v1_beta_invites__invite_id__delete","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invite Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/beta-invites/{token}/preview":{"get":{"tags":["beta-invites"],"summary":"Preview Beta Invite","description":"Tell the public landing page whether a link is still usable. No auth.\n\nRead-only — previewing never consumes the invite. Reveals nothing about the\ninviter.\n\nArgs:\n    token: The plaintext token from the ``/join/{token}`` URL.\n    request: FastAPI request (caller IP for the rate-limit bucket).\n    db: Database session.\n\nReturns:\n    ``{\"valid\": true, \"expires_at\": ...}`` for a usable link.\n\nRaises:\n    RateLimitError: 429 when the caller's per-IP bucket is exhausted.\n    NotFoundException: 404 — unknown, revoked, or malformed token.\n    BetaInviteGoneError: 410 — expired or already redeemed.","operationId":"preview_beta_invite_api_v1_beta_invites__token__preview_get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BetaInvitePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/referrals/me":{"get":{"tags":["referrals"],"summary":"Get My Referral","description":"Return the caller's referral code and standing, minting a code on first call.\n\nArgs:\n    user: Authenticated session user.\n    db: Database session.\n\nReturns:\n    The caller's referral summary.","operationId":"get_my_referral_api_v1_referrals_me_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/referrals/redeem":{"post":{"tags":["referrals"],"summary":"Redeem Referral","description":"Redeem a referral code and credit both parties.\n\nRefusals are 400s with a ``REFERRAL-*`` error code (see\n``utils/exceptions.py``); they are deliberately uniform so probing a code\nreveals nothing about whether it exists or who owns it.\n\nArgs:\n    payload: The submitted code.\n    user: Authenticated session user (the invitee).\n    db: Database session.\n\nReturns:\n    The created grant.","operationId":"redeem_referral_api_v1_referrals_redeem_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/referrals":{"get":{"tags":["admin-referrals"],"summary":"List Referral Grants","description":"List referral grants, newest first.\n\nArgs:\n    user: Authenticated system admin.\n    referrer_user_id: Restrict to a single inviter.\n    include_revoked: Include already-revoked rows (default true).\n    limit: Page size.\n    offset: Page offset.\n    db: Database session.\n\nReturns:\n    A page of ledger rows plus the total count.","operationId":"list_referral_grants_api_v1_admin_referrals_get","parameters":[{"name":"referrer_user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer User Id"}},{"name":"include_revoked","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Revoked"}},{"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/AdminReferralListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/referrals/{grant_id}/revoke":{"post":{"tags":["admin-referrals"],"summary":"Revoke Referral Grant","description":"Revoke a grant and recompute both sides' referral bonuses.\n\nIdempotent: revoking an already-revoked grant is a no-op that still returns\nthe row, so an admin retry (or a double-click) is harmless.\n\nArgs:\n    grant_id: The ledger row to revoke.\n    payload: Revocation reason.\n    user: Authenticated system admin.\n    db: Database session.\n\nReturns:\n    The revoked grant.\n\nRaises:\n    HTTPException: 404 if no such grant exists.","operationId":"revoke_referral_grant_api_v1_admin_referrals__grant_id__revoke_post","parameters":[{"name":"grant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Grant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminReferralGrantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/bm25-drift":{"get":{"tags":["bm25-drift"],"summary":"List Drift Logs","description":"List BM25 IDF drift log rows. Admin-only.\n\nSorted by measured_at DESC. Filterable by psi_status and context_id.","operationId":"list_drift_logs_api_v1_admin_bm25_drift_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/bm25-drift/{row_id}":{"get":{"tags":["bm25-drift"],"summary":"Get Drift Log Detail","description":"Detail view including top_divergent_terms. Admin-only.\n\nEmits an audit log event on every read so historical access is\nreconstructable from logs alone.","operationId":"get_drift_log_detail_api_v1_admin_bm25_drift__row_id__get","parameters":[{"name":"row_id","in":"path","required":true,"schema":{"type":"integer","title":"Row Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/bm25-drift/run":{"post":{"tags":["bm25-drift"],"summary":"Run Drift Now","description":"Manually trigger a drift measurement. Admin-only.\n\nFor dev/staging verification while the production cron is disabled\n(v0.12.1 ships infrastructure-only; production enablement = v0.14.0).\nEach requested context is enqueued as an `asyncio.create_task` and\nruns against an independent DB session.","operationId":"run_drift_now_api_v1_admin_bm25_drift_run_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftRunRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftRunResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/bm25-drift/{row_id}/reveal-terms":{"post":{"tags":["bm25-drift"],"summary":"Reveal Drift Terms","description":"Resolve mmh3-hashed term indices in top_divergent_terms to plaintext.\n\nReads pre-tokenized fields from the Qdrant payload (see\nQDRANT_TOKEN_PAYLOAD_FIELDS) populated by memory_service, recomputes\nmmh3 hashes via utils.sparse_vector.hash_token, and matches against\nthe stored indices. Falls back to re-tokenizing summary text only\nfor legacy points written before the token payload fields existed.\nStreams Qdrant pages and breaks as soon as every requested hash is\nresolved, so memory stays bounded for million-point contexts.\nRate-limited per user. Every terminal branch (success / 404 /\nrate-limited) writes an AuditLog row; term content is never logged.","operationId":"reveal_drift_terms_api_v1_admin_bm25_drift__row_id__reveal_terms_post","parameters":[{"name":"row_id","in":"path","required":true,"schema":{"type":"integer","title":"Row Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftRevealRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Bm25DriftRevealResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/reserve":{"post":{"tags":["files"],"summary":"Reserve Upload","description":"Reserve quota and return a presigned PUT URL for the upload.\n\nThe client computes the sha256 ahead of time so the server can dedup\nagainst the active set on this workspace. Repeated calls with the\nsame sha256 from the same workspace return 409; when the existing file\nis in the same context as the request and is ``uploaded``, the 409 body\ncarries it as a top-level ``existing_file`` (``FileObjectOut``) so the\nSDK can use it instead of uploading again (#1693). An earlier upload\nthat has not completed (``reserved``) is not handed out, and a\nduplicate in another context names no file (#1136).","operationId":"reserve_upload_api_v1_files_reserve_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileReserveRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileReserveResponse"}}}},"409":{"description":"The workspace already holds an active file with this sha256 (`RES-002`). When that file is in the same context as the request (both unbound counts as the same) and its upload has completed (`status` `uploaded`), the body also carries it as a top-level `existing_file` object; otherwise it is omitted."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{file_id}/confirm":{"post":{"tags":["files"],"summary":"Confirm Upload","description":"Verify the upload landed in R2 and finalize the row.\n\nIdempotent: confirming an already-uploaded file with a matching\nsha256 returns the existing row unchanged.","operationId":"confirm_upload_api_v1_files__file_id__confirm_post","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"workspace_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Owning workspace","title":"Workspace Id"},"description":"Owning workspace"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileConfirmRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileObjectOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{file_id}/download-url":{"get":{"tags":["files"],"summary":"Get Download Url","description":"Return a short-lived presigned GET URL.","operationId":"get_download_url_api_v1_files__file_id__download_url_get","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"workspace_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Owning workspace","title":"Workspace Id"},"description":"Owning workspace"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FileDownloadUrlOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files/{file_id}":{"delete":{"tags":["files"],"summary":"Delete File","description":"Soft-delete the file and immediately release the quota.\n\nThe R2 binary lingers for 7 days (sweeper handles deletion); the\nworkspace counter is decremented in the same transaction (R5).","operationId":"delete_file_api_v1_files__file_id__delete","parameters":[{"name":"file_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"File Id"}},{"name":"workspace_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Owning workspace","title":"Workspace Id"},"description":"Owning workspace"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/files":{"get":{"tags":["files"],"summary":"List Files","description":"List uploaded, non-deleted files in the workspace, newest first.","operationId":"list_files_api_v1_files_get","parameters":[{"name":"workspace_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Owning workspace","title":"Workspace Id"},"description":"Owning workspace"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/FileObjectOut"},"title":"Response List Files Api V1 Files Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/external-keys":{"get":{"tags":["external-keys"],"summary":"List External Keys","description":"List all external API keys for the current workspace.\n\nIssue #381: owner-only (router-level dependency); members, admins, and viewers\nare rejected with 403 before reaching this handler.\nIssue #385: workspace-scoped — returns every key registered in\n`current_workspace_id` regardless of the original creator's user_id, since\nthe workspace owner is the sole manager of these keys.\n\nReturns masked values for security.","operationId":"list_external_keys_api_v1_external_keys_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["external-keys"],"summary":"Create External Key","description":"Create a new external API key for the current workspace.\n\nIssue #381: owner-only (router-level dependency).\nIssue #385: workspace-scoped — the new key is stored with the caller's\ncurrent_workspace_id and context_id=None. Per-workspace uniqueness is\nenforced by (a) the app-layer pre-checks in this handler and (b) the\npartial unique index on (workspace_id, context_id, provider) WHERE enabled=true (NULLS NOT DISTINCT).","operationId":"create_external_key_api_v1_external_keys_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/external-keys/{key_name}":{"put":{"tags":["external-keys"],"summary":"Update External Key","description":"Update an external API key value in the current workspace.\n\nIssue #381: owner-only (router-level dependency).\nIssue #385: workspace-scoped lookup by (key_name, workspace_id). Any owner\nof the workspace can update any key registered in it, including keys\noriginally registered by a previous owner.","operationId":"update_external_key_api_v1_external_keys__key_name__put","parameters":[{"name":"key_name","in":"path","required":true,"schema":{"type":"string","title":"Key Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["external-keys"],"summary":"Delete External Key","description":"Delete an external API key from the current workspace.\n\nIssue #381: owner-only (router-level dependency).\nIssue #385: workspace-scoped lookup by (key_name, workspace_id); any owner\ncan delete any key registered in the workspace.\nIssue #149 / #1613: a protected key is refused with 400. Membership in\nPROTECTED_KEYS alone no longer protects a key — see\nservices/external_key_protection.py.","operationId":"delete_external_key_api_v1_external_keys__key_name__delete","parameters":[{"name":"key_name","in":"path","required":true,"schema":{"type":"string","title":"Key Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/external-keys/{key_name}/toggle":{"patch":{"tags":["external-keys"],"summary":"Toggle External Key","description":"Toggle enabled/disabled state without re-entering API key value.\n\nIssue #105: Enable/disable keys for reranker provider switching.\nIssue #246: current_context_id removed - use None\n\nRules:\n- A protected key cannot be disabled (Issue #1613: OPENAI_API_KEY, or any\n  key whose provider is openai, while OpenAI embeddings are in use — see\n  services/external_key_protection.py)\n- Only ONE reranker (Cohere/Voyage) can be enabled at a time\n\nIssue #381: Owner-only (router-level dependency).","operationId":"toggle_external_key_api_v1_external_keys__key_name__toggle_patch","parameters":[{"name":"key_name","in":"path","required":true,"schema":{"type":"string","title":"Key Name"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyToggle"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExternalKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config":{"get":{"tags":["config"],"summary":"Get All Config","description":"Get all configuration values.\n\nEvery value is the EFFECTIVE one the running process uses (Issue #1580);\n``config_overrides`` rows are never consulted — nothing reads them at\nruntime, so showing one would display a value that is not in effect.\nAdmin-only categories are omitted for non-admin callers.\n\nArgs:\n    user: Authenticated user\n    mask_sensitive: Whether to mask sensitive values (URL credentials are\n        masked regardless)\n\nReturns:\n    List of configuration values","operationId":"get_all_config_api_v1_config_get","parameters":[{"name":"mask_sensitive","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Mask Sensitive"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/categories":{"get":{"tags":["config"],"summary":"Get Categories","description":"Get configuration categories.\n\nArgs:\n    user: Authenticated user\n\nReturns:\n    Configuration categories","operationId":"get_categories_api_v1_config_categories_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/{key}":{"put":{"tags":["config"],"summary":"Update Config","description":"Refuse to update a configuration value (Issue #1580).\n\nAdmin-only endpoint, kept so existing clients get a clear answer. Every key\nis env-backed: it is set via environment variables and applied on\nrestart/redeploy, and no runtime consumer reads a stored override.\n\nArgs:\n    key: Configuration key the caller tried to update\n    request: Rejected value\n    admin: Authenticated admin user\n\nRaises:\n    ConfigReadOnlyError: Always (409 ``CFG-002``)","operationId":"update_config_api_v1_config__key__put","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"409":{"description":"Always: configuration is read-only (`CFG-002`). Values are set via environment variables and applied on restart/redeploy."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/batch":{"post":{"tags":["config"],"summary":"Batch Update Config","description":"Refuse to batch update configuration values (Issue #1580).\n\nAdmin-only endpoint. The whole request is refused — there are no partial\nwrites. See ``update_config``.\n\nArgs:\n    request: Rejected key-value pairs\n    admin: Authenticated admin user\n\nRaises:\n    ConfigReadOnlyError: Always (409 ``CFG-002``)","operationId":"batch_update_config_api_v1_config_batch_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigBatchRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"409":{"description":"Always: configuration is read-only (`CFG-002`). Values are set via environment variables and applied on restart/redeploy."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/validate":{"post":{"tags":["config"],"summary":"Validate Config","description":"Validate a configuration value.\n\nArgs:\n    request: Key and value to validate\n    user: Authenticated user\n\nReturns:\n    Validation result","operationId":"validate_config_api_v1_config_validate_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfigValidateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/config/schema":{"get":{"tags":["config"],"summary":"Get Schema","description":"Get configuration schema with metadata.\n\nReturns metadata for all configuration keys including:\n- Type (string/number/boolean/enum)\n- Valid values for ENUM types\n- Min/max ranges for numeric types\n- Descriptions, impacts, examples, recommendations\n- Restart requirements\n\nIssue #53 - Enable frontend display improvements (read-only)\n\nReturns:\n    Dictionary of configuration key schemas","operationId":"get_schema_api_v1_config_schema_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/contexts":{"get":{"tags":["contexts"],"summary":"List Contexts","description":"List all contexts for current user.\n\nReturns:\n    List of contexts (Issue #246: no current context indicator)","operationId":"list_contexts_api_v1_contexts_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contexts"],"summary":"Create Context","description":"Create a new context.\n\nIssue #115 Phase B: Contexts are created under user's current workspace.\n\nArgs:\n    request: Context creation request\n\nReturns:\n    Created context data\n\nRaises:\n    400: If context name is invalid or already exists\n    400: If user has no workspace","operationId":"create_context_api_v1_contexts_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/current":{"get":{"tags":["contexts"],"summary":"Get Current Context Removed","description":"Issue #246: Endpoint removed - context must be explicit.","operationId":"get_current_context_removed_api_v1_contexts_current_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/contexts/{context_id}":{"get":{"tags":["contexts"],"summary":"Get Context","description":"Get a specific context.\n\nArgs:\n    context_id: Context UUID\n\nReturns:\n    Context data\n\nRaises:\n    404: If project not found","operationId":"get_context_api_v1_contexts__context_id__get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["contexts"],"summary":"Update Context","description":"Update a context's display_name, description and settings.\n\nPermission (Issue #165 Phase 3):\n- summary/usage_guide/is_private/is_public/resource_id/is_locked/sleep_mode:\n  Requires OWNER access (owner-only fields — see ``owner_only_fields`` list)\n- display_name/description: Requires EDITOR access\n\nArgs:\n    context_id: Context UUID\n    request: Update request\n    user: Authenticated user\n    service: Context service\n    db: Database session\n\nReturns:\n    Updated context data\n\nRaises:\n    403: If insufficient permissions\n    404: If context not found","operationId":"update_context_api_v1_contexts__context_id__put","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["contexts"],"summary":"Delete Context","description":"Delete a context and its collection.\n\nOnly context owner can delete their context. The context, its memories and\nits files are soft-deleted together and the files' bytes leave the storage\nquota at once (#1960); an admin restore within 6 days brings the files back\nunless the same content was uploaded again in the meantime.\n\nArgs:\n    context_id: Context UUID to delete\n\nRaises:\n    400: If trying to delete default context\n    403: If not context owner\n    404: If context not found","operationId":"delete_context_api_v1_contexts__context_id__delete","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/stats":{"get":{"tags":["contexts"],"summary":"Get Context Stats","description":"Get context statistics (memory count, storage).\n\nArgs:\n    context_id: Context UUID\n\nReturns:\n    Context statistics\n\nRaises:\n    404: If project not found","operationId":"get_context_stats_api_v1_contexts__context_id__stats_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/export":{"get":{"tags":["contexts"],"summary":"Export Context","description":"Export a context as portable JSON (Issue #950 — GDPR Art.20 portability).\n\nReturns the context metadata, its search configuration, and every memory\nvisible to the caller — a private context exports only the caller's own\nmemories, a shared context exports every member's, exactly as\n``GET /memory/list`` scopes them. Vectors, neural edges, and sessions are\nintentionally omitted: they are regenerated / re-learned on re-import.\n\nThis is a context-scoped portability export (anti-lock-in), not a per-user\nArt.20 subject-access request; a created_by-filtered cross-context export\nis a planned follow-up, as is workspace-wide export.\n\nRaises:\n    404: context not found, or the caller lacks read access (uniform —\n        does not reveal whether the context exists in another workspace).\n    413: the context exceeds the single-export memory cap.","operationId":"export_context_api_v1_contexts__context_id__export_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextExportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/tags":{"get":{"tags":["contexts"],"summary":"List Context Tags","description":"List existing tag vocabulary in a context (Issue #614).\n\nSurfaces aggregated tags with usage counts and last-used timestamps so\nLLM clients can discover existing tag spellings before calling\n``remember()`` (avoid drift: ``troubleshoot`` vs ``troubleshooting``) or\nbuild accurate ``recall(filters={\"tags\": [...]})`` filters. Empty\ncontexts return ``200`` with ``tags=[]`` (no 404). Soft-deleted memories\nare excluded, and the workspace boundary is honored for shared contexts.\n\nArgs:\n    context_id: Target context UUID.\n    limit: Max tags returned (1-500).\n    min_count: Minimum memory count per tag (default 1).\n    sort: Sort mode (``count`` | ``recent`` | ``alpha``).\n    prefix: Case-insensitive literal prefix filter.\n\nRaises:\n    404: Context not found or caller has no access.\n    422: Invalid ``sort`` or ``min_count`` (FastAPI bound checks).","operationId":"list_context_tags_api_v1_contexts__context_id__tags_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum tags to return.","default":50,"title":"Limit"},"description":"Maximum tags to return."},{"name":"min_count","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"description":"Minimum memory count per tag. Default 1 surfaces one-off tags so drift typos are visible; raise to hide low-frequency noise.","default":1,"title":"Min Count"},"description":"Minimum memory count per tag. Default 1 surfaces one-off tags so drift typos are visible; raise to hide low-frequency noise."},{"name":"sort","in":"query","required":false,"schema":{"enum":["count","recent","alpha"],"type":"string","description":"Sort: count (default), recent, or alpha (lower-case).","default":"count","title":"Sort"},"description":"Sort: count (default), recent, or alpha (lower-case)."},{"name":"prefix","in":"query","required":false,"schema":{"type":"string","maxLength":200,"description":"Case-insensitive prefix filter for autocomplete. `%` / `_` are escaped to literal characters — the parameter cannot be used as a wildcard probe.","default":"","title":"Prefix"},"description":"Case-insensitive prefix filter for autocomplete. `%` / `_` are escaped to literal characters — the parameter cannot be used as a wildcard probe."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Optional case-insensitive substring filter on memory.summary (#618). When set, only tags on matching memories are aggregated, so the tag cloud facets to the current search. Whitespace-only is treated as no filter.","title":"Q"},"description":"Optional case-insensitive substring filter on memory.summary (#618). When set, only tags on matching memories are aggregated, so the tag cloud facets to the current search. Whitespace-only is treated as no filter."},{"name":"with_tags","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"description":"Repeatable tag filter for multi-tag AND drill-down (#830). When set, only tags that co-occur on memories carrying ALL of these tags are aggregated, and the with_tags values themselves are excluded from the result. AND-combined with `q`. Counts reflect the faceted (co-occurrence) subset.","title":"With Tags"},"description":"Repeatable tag filter for multi-tag AND drill-down (#830). When set, only tags that co-occur on memories carrying ALL of these tags are aggregated, and the with_tags values themselves are excluded from the result. AND-combined with `q`. Counts reflect the faceted (co-occurrence) subset."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextTagsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/files":{"delete":{"tags":["contexts"],"summary":"Delete Context Files","description":"Soft-delete every file of the context and release their quota (#1960).\n\nOwner only. Allowed while the workspace is capacity-locked (#1941): it is\ncleanup, the way an owner gets back under the storage limit — and the\ndefault context cannot be deleted, so this is how its files are cleared in\nbulk. The files take the same road as a single ``DELETE /files/{id}``: the\nrows are soft-deleted now, the R2 objects go with the 7-day GC.\n\nRaises:\n    403: Not the context owner.\n    404: Context not found.\n    409: The context is locked (``is_locked``), as for deleting it.","operationId":"delete_context_files_api_v1_contexts__context_id__files_delete","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextFilesDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/members":{"get":{"tags":["contexts"],"summary":"List Context Members","description":"List all members who have access to context.\n\nReturns both explicit context members and workspace owners/admins\n(who have automatic access).\n\nRequires viewer access to context.","operationId":"list_context_members_api_v1_contexts__context_id__members_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ContextMemberResponse"},"title":"Response List Context Members Api V1 Contexts  Context Id  Members Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["contexts"],"summary":"Add Context Member","description":"Add a member to context.\n\nRequires owner access to context (or workspace admin/owner).","operationId":"add_context_member_api_v1_contexts__context_id__members_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddContextMemberRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextMemberResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/members/{user_id}":{"put":{"tags":["contexts"],"summary":"Update Context Member Role","description":"Update context member's role.\n\nRequires owner access to context (or workspace admin/owner).","operationId":"update_context_member_role_api_v1_contexts__context_id__members__user_id__put","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","title":"User Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateContextMemberRoleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextMemberResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["contexts"],"summary":"Remove Context Member","description":"Remove member from context.\n\nRequires owner access to context (or workspace admin/owner).\nCannot remove context owner.","operationId":"remove_context_member_api_v1_contexts__context_id__members__user_id__delete","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context 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/v1/contexts/{context_id}/memory-stats":{"get":{"tags":["contexts"],"summary":"Get Memory Usage Stats","description":"Get per-memory recall statistics for a context.\n\nIssue #83: Memory usage stats for context cleanup workflows.\nSupports sorting by access_count, reference_count, importance, created_at,\nlast_used_at (issue #1046 replaced the dead ``use_count`` sort with the\nadoption-signal ``reference_count``; default sort moved to ``access_count``).","operationId":"get_memory_usage_stats_api_v1_contexts__context_id__memory_stats_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"sort_by","in":"query","required":false,"schema":{"type":"string","description":"Sort field","default":"access_count","title":"Sort By"},"description":"Sort field"},{"name":"sort_order","in":"query","required":false,"schema":{"type":"string","description":"Sort order: asc or desc","default":"desc","title":"Sort Order"},"description":"Sort order: asc or desc"},{"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":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryUsageStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/duplicates":{"get":{"tags":["contexts"],"summary":"Find Duplicates","description":"Find duplicate memory pairs using Qdrant vector similarity.\n\nIssue #83: Duplicate detection for context cleanup.\nScans recent memories (max 200) and finds pairs above similarity threshold.","operationId":"find_duplicates_api_v1_contexts__context_id__duplicates_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"threshold","in":"query","required":false,"schema":{"type":"number","maximum":1.0,"minimum":0.5,"default":0.9,"title":"Threshold"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DuplicatesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/search-config":{"get":{"tags":["context-search-config"],"summary":"Get Context Search Config","description":"Get context search configuration.\n\nRetrieves hybrid search weights, fetch factor, and reranker settings for a context.\n\nArgs:\n    context_id: Context UUID\n    current_user: Current authenticated user\n    db: Database session\n\nReturns:\n    ContextSearchConfigResponse with all settings\n\nRaises:\n    HTTPException: 404 if context not found","operationId":"get_context_search_config_api_v1_contexts__context_id__search_config_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextSearchConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["context-search-config"],"summary":"Update Context Search Config","description":"Update context search configuration.\n\nUpdates hybrid search weights, fetch factor, and/or reranker settings.\nValidates that weights sum to 1.0 and model matches provider.\n\nContext owners and editors may change every field except\n``content_chunks_enabled`` and ``llm_selector_enabled``: changing either\n(on or off) takes the context owner or a workspace owner/admin, since\nturning it on sends memory content to the embedding and rerank providers\n(#2015) or to the deployment's judge model at recall time (#2144). Sending\nthe stored value is not a change and is allowed.\n\nArgs:\n    context_id: Context UUID\n    update_data: Configuration update request\n    current_user: Current authenticated user\n    db: Database session\n\nReturns:\n    Updated ContextSearchConfigResponse\n\nRaises:\n    HTTPException: 400 if validation fails, 403 if the caller may not\n        change content_chunks_enabled, 404 if context not found, 500 on error\n    ValidationError: 422 when turning content_chunks_enabled on with the\n        LanceDB vector store, which has no content chunk vectors (#2072)\n    FeatureNotAvailableError: 403 ``FEAT-001`` when turning it on on a plan\n        without ``content_chunks`` (only where CONTENT_CHUNKS_REQUIRE_PLAN\n        is on, #2103)\n    QuotaExceededError: 429 ``QUOTA-001`` when turning it on with the\n        workspace at the plan's ``max_content_chunk_contexts`` (likewise).\n        The same 403 / 429 for ``llm_selector_enabled`` against\n        ``llm_selector`` / ``max_llm_selector_contexts``, only where\n        LLM_SELECTOR_REQUIRE_PLAN is on (#2144)","operationId":"update_context_search_config_api_v1_contexts__context_id__search_config_put","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextSearchConfigUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextSearchConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/search-config/reset":{"post":{"tags":["context-search-config"],"summary":"Reset Context Search Config","description":"Reset context search configuration to defaults.\n\nResets all settings to default values:\n- semantic_weight: 0.6\n- bm25_weight: 0.4\n- fetch_factor: 3\n- use_rerank / reranker_provider / reranker_model: the deployment default\n  (DEFAULT_USE_RERANK / DEFAULT_RERANKER_PROVIDER / DEFAULT_RERANKER_MODEL,\n  #1572; unset: false / 'voyage' / 'rerank-2')\n- content_chunks_enabled: false — while it is on, that is a change and takes\n  the context owner or a workspace owner/admin (#2015)\n- llm_selector_enabled: false — on the same rule (#2144)\n\nArgs:\n    context_id: Context UUID\n    current_user: Current authenticated user\n    db: Database session\n\nReturns:\n    Reset ContextSearchConfigResponse\n\nRaises:\n    HTTPException: 403 if content_chunks_enabled or llm_selector_enabled\n        is on and the caller is not the context owner, 404 if context not\n        found, 500 on error","operationId":"reset_context_search_config_api_v1_contexts__context_id__search_config_reset_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContextSearchConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/state/{key}":{"put":{"tags":["agent-state"],"summary":"Set Agent State","description":"Upsert ``value`` at ``(context_id, key)`` (PUT = idempotent, 200 on replace).","operationId":"set_agent_state_api_v1_contexts__context_id__state__key__put","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"title":"Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStateSetRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStateKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["agent-state"],"summary":"Get Agent State","description":"Read one key's live value. 404 when absent or expired.","operationId":"get_agent_state_api_v1_contexts__context_id__state__key__get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"title":"Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStateValueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["agent-state"],"summary":"Delete Agent State","description":"Delete ``(context_id, key)``. 404 when the key was not present.","operationId":"delete_agent_state_api_v1_contexts__context_id__state__key__delete","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"title":"Key"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStateKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/state":{"get":{"tags":["agent-state"],"summary":"List Agent State","description":"List all live ``key → value`` entries for the context (200, empty = {}).","operationId":"list_agent_state_api_v1_contexts__context_id__state_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentStateListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/feedback":{"post":{"tags":["retrieval-feedback"],"summary":"Record Feedback","description":"Record an append-only retrieval-feedback event for a recalled memory.","operationId":"record_feedback_api_v1_contexts__context_id__feedback_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/contexts/{context_id}/host-feedback":{"post":{"tags":["retrieval-feedback"],"summary":"Record Host Feedback","description":"Record a server-stamped host verdict from a trusted operator.","operationId":"record_host_feedback_api_v1_contexts__context_id__host_feedback_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HostFeedbackRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/tools":{"get":{"tags":["mcp-api"],"summary":"List Mcp Tools","description":"List all available MCP tools.\n\nArgs:\n    user: Authenticated user\n\nReturns:\n    List of MCP tools with descriptions and schemas","operationId":"list_mcp_tools_api_v1_mcp_tools_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPToolsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/status":{"get":{"tags":["mcp-api"],"summary":"Get Mcp Status","description":"Get MCP server status.\n\nArgs:\n    user: Authenticated user\n\nReturns:\n    MCP server status including active sessions and tools","operationId":"get_mcp_status_api_v1_mcp_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/remember":{"post":{"tags":["memory"],"summary":"Remember","description":"Store new memory with 3-layer architecture.\n\nIssue #1 specification:\n- Layer 1: summary (Embedding化、検索用)\n- Layer 2: context_summary (文脈説明)\n- Layer 3: content + details (完全詳細)\n\nRequest:\n    {\n        \"summary\": \"認証エラー修正。JWTトークン有効期限チェック追加。\",\n        \"context_summary\": \"ユーザーからログイン失敗の報告があり...\",\n        \"content\": \"auth.pyのverify_token関数にexpired_atの検証を追加\",\n        \"details\": {\"code_diff\": \"...\", \"test_results\": \"...\"},\n        \"type\": \"code\",\n        \"importance\": 0.8,\n        \"tags\": [\"python\", \"authentication\"],\n        \"context\": {\"context_id\": \"my-context\"}\n    }\n\nResponse:\n    {\n        \"status\": \"success\",\n        \"memory_id\": \"uuid\",\n        \"scope\": \"working\",\n        \"persistence\": {\n            \"scope\": \"working\",\n            \"committed\": true,\n            \"promotes_via\": \"sleep_consolidation\",\n            \"consolidation_archive_min_age_days\": 30,\n            \"detail\": \"...\"\n        }\n    }\n\nThe memory is committed before this returns; ``scope`` selects the\nconsolidation lifecycle, not whether it was stored. ``persistence``\ndescribes that lifecycle for this deployment and is null when the scope\ncannot be classified. Its age floor is scoped to consolidation and is not a\nretention guarantee — see ``services.persistence``.\n\nExample:\n    POST /api/v1/memory/remember\n    Authorization: Bearer <session_token>","operationId":"remember_api_v1_memory_remember_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RememberRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RememberResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/reference":{"post":{"tags":["memory"],"summary":"Reference","description":"Get full memory details (Layer 3).\n\nRetrieves complete memory data including:\n- summary, context_summary, content\n- details (JSONB)\n- All metadata\n\nRequest:\n    {\n        \"memory_id\": \"uuid\",\n        \"version\": 3            // optional (#2043): an earlier version\n    }\n\nResponse:\n    {\n        \"memory_id\": \"uuid\",\n        \"summary\": \"...\",\n        \"context_summary\": \"...\",\n        \"content\": \"...\",\n        \"details\": {...},\n        \"type\": \"code\",\n        \"importance\": 0.8,\n        \"tags\": [...],\n        \"context\": {...},\n        \"created_at\": \"2025-11-20T...\",\n        \"client\": \"web\"\n    }\n\nExample:\n    POST /api/v1/memory/reference\n    Authorization: Bearer <session_token>","operationId":"reference_api_v1_memory_reference_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/recall":{"post":{"tags":["memory"],"summary":"Recall","description":"Search memories with Hybrid Search.\n\nIssue #1 specification:\n- Semantic Search (OpenAI Embedding) 60%\n- BM25/Full-text (Qdrant) 40%\n- Cohere Reranking (optional)\n\n``filters.context_id`` names the context to search. It is resolved for the\ncaller first — workspace membership, suspension, the context whitelist,\nprivate-context ownership and the API key's workspace scope — with the\nuniform ``404`` on any deny, the gate every other UUID-addressed read\n(``/memory/list``, ``/memory/stats``, graph, the MCP ``recall`` tool)\napplies. The search then runs against the context's own workspace.\n\nRequest:\n    {\n        \"query\": \"認証エラーの解決方法\",\n        \"k\": 5,\n        \"use_rerank\": false,\n        \"filters\": {\n            \"context_id\": \"<context-uuid>\",\n            \"scope\": \"persistent\",\n            \"type\": \"code\"\n        }\n    }\n\nExample:\n    POST /api/v1/memory/recall\n    Authorization: Bearer <session_token>","operationId":"recall_api_v1_memory_recall_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecallResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/pinned":{"post":{"tags":["memory"],"summary":"Load Pinned","description":"Deterministically load a context's always-delivery memories (Issue #886).\n\nThe deterministic counterpart to ``/recall``: returns the complete,\nunranked, ordered ``delivery_mode='always'`` set for the given context —\nno semantic ranking, no rerank. Items carry L1 + L2 only (summary +\ncontext_summary); fetch full content via ``/reference``. Bounded by\n``settings.pinned_load_cap`` (or the request ``cap``); when the pinned set\nexceeds the cap, ``truncated`` is true and ``total_available`` reports the\nreal count — never a silent truncation.\n\nRequest:\n    {\"context_id\": \"<context-uuid>\", \"cap\": 100}\n\nExample:\n    POST /api/v1/memory/pinned\n    Authorization: Bearer <session_token>","operationId":"load_pinned_api_v1_memory_pinned_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadPinnedRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadPinnedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/guardrails":{"post":{"tags":["memory"],"summary":"Load Guardrails","description":"Deterministically load a context's guardrail set for a client-side hook.\n\nThe REST twin of the MCP ``load_guardrails`` tool and ``/pinned``'s\nsibling: the trusted-tier pinned set (``delivery_mode='always'``) plus the\nmemories marked with ``details.tool_trigger``, each list ordered\n``importance DESC, created_at ASC, id ASC`` and capped on its own\n(``cap`` bounds ``tool_triggered`` only; the pinned list uses\n``settings.pinned_load_cap``). Plain SQL — no ranking, no rerank. Items are\nL1 (+ L2 on pinned items) and carry the normalized ``tool_trigger`` object\nas data; the server never runs the patterns. The response's ``format`` and\n``version`` are the shared cache contract documented with the MCP tool.\n\nRequest:\n    {\"context_id\": \"<context-uuid>\", \"cap\": 50}\n\nExample:\n    POST /api/v1/memory/guardrails\n    Authorization: Bearer <api_key>","operationId":"load_guardrails_api_v1_memory_guardrails_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadGuardrailsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LoadGuardrailsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/guardrails/digest":{"get":{"tags":["memory"],"summary":"Guardrail Digest","description":"Render a context's tool guardrails for clients without tool hooks (#1621).\n\nThe export target is the block a Codex cloud setup script (or any\nalways-loaded-file recipe) writes into ``AGENTS.md``:\n\n    <!-- kagura-memory:guardrails begin context=<uuid> tool_triggered_version=<hash> -->\n    - (<id8>) <summary>\n    <!-- kagura-memory:guardrails end -->\n\nThe instructions target previews the MCP ``instructions`` string lane (a)\nwould serve this credential for this context — ``profile`` / ``tools``\nreproduce the URL's tool view so the truncation suffix names the same tool.\nSame trusted-only read and binding filter as ``load_guardrails``, same\nuniform ``404`` on a denied, unknown or other-workspace context; an\nexternal-tier or unmarked context is a ``200`` with an empty body\n(``export``) or the base text (``instructions``). With\n``mcp_guardrail_digest_enabled`` off the preview is the base text for\nevery context — exactly what lane (a) serves then. Never cached\n(``private, no-store``); the body is editor-authored text, so it is served\nwith ``nosniff``. Contract: MCP Tools › Server instructions.\n\nExample:\n    GET /api/v1/memory/guardrails/digest?context_id=<uuid>\n    Authorization: Bearer <api_key>","operationId":"guardrail_digest_api_v1_memory_guardrails_digest_get","parameters":[{"name":"context_id","in":"query","required":true,"schema":{"type":"string","description":"Context UUID whose tool guardrails to render","title":"Context Id"},"description":"Context UUID whose tool guardrails to render"},{"name":"target","in":"query","required":false,"schema":{"type":"string","description":"export (default): the AGENTS.md block as text/markdown; instructions: the exact MCP server instructions this credential would receive","default":"export","title":"Target"},"description":"export (default): the AGENTS.md block as text/markdown; instructions: the exact MCP server instructions this credential would receive"},{"name":"profile","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"target=instructions only: the MCP URL's profile value (full | core)","title":"Profile"},"description":"target=instructions only: the MCP URL's profile value (full | core)"},{"name":"tools","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"target=instructions only: the MCP URL's tools allowlist","title":"Tools"},"description":"target=instructions only: the MCP URL's tools allowlist"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}},"text/markdown":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/{memory_id}":{"patch":{"tags":["memory"],"summary":"Patch Memory","description":"Partial update of a memory by UUID (Issue #439).\n\nAccepts any subset of ``summary``/``content``/``type``/``importance``/\n``tags``/``details``. Omitted fields preserve their current value;\n``tags`` follows replace-all semantics. ``expected_version`` (#2043)\nmakes the patch conditional on the memory's current version.\n\nStatus codes:\n    200: Updated successfully. Body is the full ``ReferenceResponse``.\n    404: Memory does not exist OR the caller lacks access (existence\n         is intentionally not leaked).\n    409: ``expected_version`` is not the memory's current version\n         (``details.reason`` = ``version_conflict``, ``details.\n         current_version``); nothing was written (#2043).\n    410: Memory was soft-deleted; the tombstone exists but the resource\n         is gone. Distinguishing 410 from 404 lets clients stop retrying.\n    422: Request body validation failed (empty patch, importance out of\n         range, summary < 10 chars, etc.).\n    429: The patched memory's post-patch size exceeds ``MAX_CONTENT_SIZE``\n         (1MB) — same envelope as ``remember`` / ``update_memory``.\n\nPermission: same envelope as ``forget`` — ``PermissionService.\ncan_access_memory`` (workspace member for shared, creator for private).\n\nEmbedding regeneration: triggered only when ``summary`` or ``content``\nchanges. Runs on the same async-task pipeline as the ``remember`` path\n(``process_pending_embedding``); this PATCH does not block on embedding\ncompletion. Neural edges anchored on this memory are invalidated when\nre-embed fires.\n\nExample:\n    PATCH /api/v1/memory/550e8400-e29b-41d4-a716-446655440000\n    Authorization: Bearer <session_token>\n    Content-Type: application/json\n\n    {\"importance\": 0.9, \"tags\": [\"python\", \"auth\"]}","operationId":"patch_memory_api_v1_memory__memory_id__patch","parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Memory Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchMemoryRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferenceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/{memory_id}/revisions":{"get":{"tags":["memory"],"summary":"List Memory Revisions","description":"A memory's revision log, newest first (#2043).\n\nOne item per write event — ``created``, ``updated`` (with the fields it\nchanged), ``forgotten``, ``restored`` — with the version after it. Read\nan earlier version's text with ``POST /reference`` and ``version``.\n\nStatus codes:\n    200: A page; ``next_cursor`` (with ``has_more``) continues it.\n    404: No such memory, the caller lacks access, or it was forgotten\n         (the same answer as ``reference``).\n    422: ``limit`` outside 1-100 or a malformed ``cursor``.\n\nPermission: the same as ``reference`` (``PermissionService.\ncan_access_memory``). No member identity is returned: ``by_you`` marks\nthe caller's own events, and only those carry ``agent_id`` /\n``session_id``.","operationId":"list_memory_revisions_api_v1_memory__memory_id__revisions_get","parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Memory Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^[0-9]{1,18}$"},{"type":"null"}],"title":"Cursor"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryRevisionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/forget":{"post":{"tags":["memory"],"summary":"Forget","description":"Delete memory.\n\nRequest:\n    {\n        \"memory_id\": \"uuid\"\n    }\n\nResponse:\n    {\n        \"status\": \"success\",\n        \"deleted_count\": 1,\n        \"memory_ids\": [\"uuid\"]\n    }\n\nExample:\n    POST /api/v1/memory/forget\n    Authorization: Bearer <session_token>","operationId":"forget_api_v1_memory_forget_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgetRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgetResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/forget-bulk":{"post":{"tags":["memory"],"summary":"Forget Bulk","description":"Delete every memory in one context that matches the filters (#1941).\n\nFilters combine with AND: ``created_before`` (exclusive),\n``created_after`` (inclusive), ``type``, ``tags`` (any of). At least one\nfilter or ``all: true`` is required. ``dry_run`` defaults to ``true`` and\nreturns ``matched``; with ``dry_run: false`` the matches are deleted the\nway ``/forget`` deletes one memory (soft delete, search point and graph\nedges removed) and ``deleted`` is returned. One request deletes at most\n2,000 memories; ``remaining: true`` means more match — repeat the request\nwith ``cursor`` set to the returned ``next_cursor``.\n\nPermissions are ``/forget``'s: write access to the context (uniform 404\notherwise), then the per-memory delete check — a memory the caller may\nnot delete is skipped and not counted. Allowed while the workspace is over\nits Free capacity: deleting is how it gets back under the cap.","operationId":"forget_bulk_api_v1_memory_forget_bulk_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgetBulkRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForgetBulkResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/explore":{"post":{"tags":["memory"],"summary":"Explore","description":"Explore related memories via graph traversal.\n\nNeural Memory graph exploration using Activation Spreading.\n\nRequest:\n    {\n        \"memory_id\": \"uuid\",\n        \"depth\": 2,\n        \"relation_types\": [\"neural_association\"],\n        \"min_weight\": 0.5\n    }\n\nResponse:\n    {\n        \"seed_memory\": {...},\n        \"related_memories\": [\n            {\n                \"memory_id\": \"uuid2\",\n                \"summary\": \"...\",\n                \"activation\": 0.85,\n                \"hop\": 1,\n                \"weight\": 0.92,\n                \"path\": [\"uuid\", \"uuid2\"]\n            }\n        ],\n        \"metadata\": {\n            \"total_activated\": 15,\n            \"returned\": 10\n        }\n    }\n\nExample:\n    POST /api/v1/memory/explore\n    Authorization: Bearer <session_token>","operationId":"explore_api_v1_memory_explore_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExploreRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExploreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/stats":{"get":{"tags":["memory"],"summary":"Get Memory Stats","description":"Get memory statistics for the authenticated user's context.\n\nIssue #82: Now context-scoped - returns stats for specified or current context.\n\nArgs:\n    user: Authenticated user\n    context_id: Optional context ID (defaults to current context)\n    memory_service: Memory service instance\n\nReturns:\n    Memory statistics for specified or current context including counts, types, and storage","operationId":"get_memory_stats_api_v1_memory_stats_get","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Optional context ID (defaults to current context)","title":"Context Id"},"description":"Optional context ID (defaults to current context)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/list":{"get":{"tags":["memory"],"summary":"List Memories","description":"List memories for the authenticated user.\n\nArgs:\n    user: Authenticated user\n    db: Database session\n    scope: Filter by scope (working or persistent)\n    type: Filter by memory type\n    context_id: Optional context ID. When present, the caller must have\n        workspace-read access (uniform 404 via ``PermissionService`` on\n        denial / non-existence). For shared contexts every member sees\n        every memory regardless of original creator; for private contexts\n        only the creator sees their own. Mirrors the graph routes'\n        ``owner_filter = user_id if context.is_private else None`` pattern\n        in ``api/routes/graph.py``.\n    q: Optional case-insensitive substring filter applied to\n        ``Memory.summary`` via SQL ``ILIKE``. Surrounding whitespace is\n        stripped, and whitespace-only / empty values are normalized to\n        ``None`` (no filter). Independent of ``owner_filter`` — works\n        for both private (creator-only) and shared (all-members)\n        contexts.\n    limit: Maximum number of memories to return\n    offset: Pagination offset\n\nReturns:\n    Paginated list of memories","operationId":"list_memories_api_v1_memory_list_get","parameters":[{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(working|persistent)$"},{"type":"null"}],"title":"Scope"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"}},{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Optional context ID to scope results to a single context","title":"Context Id"},"description":"Optional context ID to scope results to a single context"},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Optional case-insensitive substring filter on memory.summary. Whitespace-only values are treated as None.","title":"Q"},"description":"Optional case-insensitive substring filter on memory.summary. Whitespace-only values are treated as None."},{"name":"tags","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter to memories having ANY of these tags (exact match). Repeat the param to pass several: ?tags=a&tags=b. Combined with other filters (e.g. q) by AND. Blank / whitespace-only entries are ignored.","title":"Tags"},"description":"Filter to memories having ANY of these tags (exact match). Repeat the param to pass several: ?tags=a&tags=b. Combined with other filters (e.g. q) by AND. Blank / whitespace-only entries are ignored."},{"name":"tags_match","in":"query","required":false,"schema":{"type":"string","pattern":"^(any|all)$","description":"How to combine `tags`: `any` (default — memory has at least one of the tags, PG array overlap; preserves #618 behavior) or `all` (memory holds every given tag, PG array contains — #830 drill-down).","default":"any","title":"Tags Match"},"description":"How to combine `tags`: `any` (default — memory has at least one of the tags, PG array overlap; preserves #618 behavior) or `all` (memory holds every given tag, PG array contains — #830 drill-down)."},{"name":"trigger_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Time Memory (#877) window lower bound (naive ISO, e.g. 2026-07-01T00:00:00). Selects type='time' memories whose stored [trigger_from, trigger_until] window overlaps the query window. Pass 'now' here to get upcoming items.","title":"Trigger From"},"description":"Time Memory (#877) window lower bound (naive ISO, e.g. 2026-07-01T00:00:00). Selects type='time' memories whose stored [trigger_from, trigger_until] window overlaps the query window. Pass 'now' here to get upcoming items."},{"name":"trigger_until","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Time Memory (#877) window upper bound (naive ISO). Omit for an open-ended (future) window.","title":"Trigger Until"},"description":"Time Memory (#877) window upper bound (naive ISO). Omit for an open-ended (future) window."},{"name":"lat_min","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":90,"minimum":-90},{"type":"null"}],"description":"WHERE-axis (#1334) bbox lower latitude bound (degrees). Any bbox bound restricts results to memories with a complete location (generated columns from details.location); bounds may be one-sided.","title":"Lat Min"},"description":"WHERE-axis (#1334) bbox lower latitude bound (degrees). Any bbox bound restricts results to memories with a complete location (generated columns from details.location); bounds may be one-sided."},{"name":"lat_max","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":90,"minimum":-90},{"type":"null"}],"description":"Bbox upper latitude bound.","title":"Lat Max"},"description":"Bbox upper latitude bound."},{"name":"lon_min","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":180,"minimum":-180},{"type":"null"}],"description":"Bbox lower longitude bound. lon_min > lon_max selects the antimeridian-crossing (±180°) box: lon >= lon_min OR lon <= lon_max.","title":"Lon Min"},"description":"Bbox lower longitude bound. lon_min > lon_max selects the antimeridian-crossing (±180°) box: lon >= lon_min OR lon <= lon_max."},{"name":"lon_max","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":180,"minimum":-180},{"type":"null"}],"description":"Bbox upper longitude bound (see lon_min for the antimeridian-crossing form).","title":"Lon Max"},"description":"Bbox upper longitude bound (see lon_min for the antimeridian-crossing form)."},{"name":"order_by","in":"query","required":false,"schema":{"type":"string","pattern":"^(created_at|trigger_from)$","description":"Sort key. 'created_at' (default, desc) or 'trigger_from' (asc) for upcoming-first Time Memory listing (#877).","default":"created_at","title":"Order By"},"description":"Sort key. 'created_at' (default, desc) or 'trigger_from' (asc) for upcoming-first Time Memory listing (#877)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MemoryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/memory/access-patterns":{"get":{"tags":["memory"],"summary":"Get Access Patterns","description":"Get memory access patterns and analytics for specified or current context.\n\nIssue #82: Now context-scoped - returns patterns for specified or current context.\n\nReturns most accessed memories, type distribution, and access timeline.\n\nArgs:\n    user: Authenticated user\n    context_id: Optional context ID (defaults to current context)\n    db: Database session\n    days: Number of days to analyze (default: 30)\n\nReturns:\n    Access pattern statistics for specified or current context","operationId":"get_access_patterns_api_v1_memory_access_patterns_get","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Optional context ID (defaults to current context)","title":"Context Id"},"description":"Optional context ID (defaults to current context)"},{"name":"days","in":"query","required":false,"schema":{"type":"integer","default":30,"title":"Days"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Get Access Patterns Api V1 Memory Access Patterns Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/attachments/memories/{memory_id}":{"post":{"tags":["attachments"],"summary":"Upload Attachment Gone","operationId":"upload_attachment_gone_api_v1_attachments_memories__memory_id__post","deprecated":true,"parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","title":"Memory Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"410":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["attachments"],"summary":"List Attachments Gone","operationId":"list_attachments_gone_api_v1_attachments_memories__memory_id__get","deprecated":true,"parameters":[{"name":"memory_id","in":"path","required":true,"schema":{"type":"string","title":"Memory Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"410":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/attachments/{attachment_id}":{"get":{"tags":["attachments"],"summary":"Download Attachment Gone","operationId":"download_attachment_gone_api_v1_attachments__attachment_id__get","deprecated":true,"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"410":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["attachments"],"summary":"Delete Attachment Gone","operationId":"delete_attachment_gone_api_v1_attachments__attachment_id__delete","deprecated":true,"parameters":[{"name":"attachment_id","in":"path","required":true,"schema":{"type":"string","title":"Attachment Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"410":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources/{resource_id}/events":{"post":{"tags":["resource-ingest"],"summary":"Ingest Event","description":"Ingest a single resource event (upsert or delete).\n\nIssue #238: Append-only event log for incremental indexing.\n\nThe ``resource_id`` URL path parameter accepts the human-readable slug\n(e.g. ``my-github-repo``). Internally, token verification resolves the\nrequest context from that identifier (see ``verify_resource_token`` and\n``_resolve_authoritative_context``), and workspace access is enforced as\na separate validation step. The authoritative resource record used for\nwrites is then taken from the verified token record\n(``token_record.resource_pk``), so external callers never need to know or\nsupply UUIDs.\n\nSecurity:\n    - Requires X-Resource-API-Key header\n    - Token must be scoped to this resource_id\n    - Rate limited by quota_events_per_hour\n\nRequest:\n    POST /api/v1/resources/{resource_id}/events\n    Headers:\n        X-Resource-API-Key: kagura_resource_...\n    Body:\n        {\n            \"op\": \"upsert\",\n            \"doc_id\": \"PROD-12345\",\n            \"version\": 3,\n            \"payload\": {\"product_name\": \"...\", \"price\": 5980},\n            \"idempotency_key\": \"optional-key\"\n        }\n\nResponse:\n    {\n        \"status\": \"success\",\n        \"event_id\": 12345,\n        \"queued\": true,\n        \"estimated_indexing_time_seconds\": 600\n    }\n\nErrors:\n    - 401: Invalid/revoked token\n    - 409: Duplicate version (resource_id, doc_id, version already exists)\n    - 413: Payload too large (>100KB)\n    - 422: Validation error (e.g., missing payload for upsert)\n    - 429: Quota exceeded","operationId":"ingest_event_api_v1_resources__resource_id__events_post","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"X-Resource-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Resource-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceEventRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceEventResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["resources"],"summary":"List Resource Events Route","description":"Browse ingested events for a resource, newest first (cursor-paginated).\n\nOwner-only (``WorkspaceOwner``) for parity with the rest of the resources\nsurface (#389). Events are scoped to the caller's workspace via the\nauthoritative ``resource_pk``; a slug that does not resolve to a Resource\nin this workspace returns an empty page (CWE-639 fail-safe).\n\nExample:\n    GET /api/v1/resources/ec-products/events?op=upsert&limit=20","operationId":"list_resource_events_route_api_v1_resources__resource_id__events_get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"op","in":"query","required":false,"schema":{"anyOf":[{"enum":["upsert","delete"],"type":"string"},{"type":"null"}],"description":"Filter by operation type","title":"Op"},"description":"Filter by operation type"},{"name":"doc_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by exact document id","title":"Doc Id"},"description":"Filter by exact document id"},{"name":"version","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Filter by exact document version","title":"Version"},"description":"Filter by exact document version"},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Only events created at or after this ISO 8601 timestamp","title":"Since"},"description":"Only events created at or after this ISO 8601 timestamp"},{"name":"limit","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":100,"minimum":1},{"type":"null"}],"description":"Page size (default 20, max 100)","title":"Limit"},"description":"Page size (default 20, max 100)"},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque cursor from a previous response's next_cursor","title":"Cursor"},"description":"Opaque cursor from a previous response's next_cursor"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceEventsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources/{resource_id}/events/batch":{"post":{"tags":["resource-ingest"],"summary":"Ingest Batch","description":"Ingest multiple resource events in a single request.\n\nIssue #238: Batch ingestion for efficiency (up to 100 events).\n\nSecurity:\n    - Same as single event ingestion\n    - Quota checked for entire batch\n\nRequest:\n    POST /api/v1/resources/{resource_id}/events/batch\n    Headers:\n        X-Resource-API-Key: kagura_resource_...\n    Body:\n        {\n            \"events\": [\n                {\"op\": \"upsert\", \"doc_id\": \"PROD-1\", \"version\": 1, \"payload\": {...}},\n                {\"op\": \"delete\", \"doc_id\": \"PROD-999\", \"version\": 5}\n            ]\n        }\n\nResponse:\n    {\n        \"status\": \"success\",\n        \"created_count\": 2,\n        \"failed_count\": 0,\n        \"event_ids\": [12345, 12346],\n        \"errors\": []\n    }\n\nErrors:\n    - 401: Invalid/revoked token\n    - 403: Cross-tenant ingest blocked (Issue #322)\n    - 404: Resource not bound to an active Context\n    - 422: Validation error (max 100 events)\n    - 429: Quota exceeded","operationId":"ingest_batch_api_v1_resources__resource_id__events_batch_post","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"X-Resource-API-Key","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"X-Resource-Api-Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceEventBatchRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceEventBatchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources/{resource_id}/schema":{"get":{"tags":["resource-schema"],"summary":"Get Schema","description":"Get resource schema (latest or specific version).\n\nOwner-only (#389): ``WorkspaceOwner`` rejects non-owners with 403;\n``resolve_resource_by_slug`` returns 404 on cross-workspace probes.\n\nExample:\n    GET /api/v1/resources/ec_products/schema\n    GET /api/v1/resources/ec_products/schema?schema_version=2","operationId":"get_schema_api_v1_resources__resource_id__schema_get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"schema_version","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Schema Version"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchemaResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["resource-schema"],"summary":"Create Schema","description":"Create a new resource schema version.\n\nIssue #276: Owner-only access via WorkspaceOwner dependency.\n\nArgs:\n    resource_id: Resource identifier\n    request: Schema creation request\n    owner: Workspace owner (user_id, workspace_id) from dependency\n    db: Database session\n\nReturns:\n    Created schema\n\nRaises:\n    403: Not workspace owner\n    400: Invalid resource_id or schema definition\n    404: No such resource in the caller's workspace\n    409: The resource has no live context (#1877)\n\nNotes:\n    - Automatically increments schema_version\n    - Only workspace owners can create schemas\n    - Triggers rebuild for all contexts using this resource (TODO: Week 4)\n\nExample:\n    POST /api/v1/resources/ec_products/schema\n    Body:\n    {\n        \"resource_id\": \"ec_products\",\n        \"field_definitions\": [\n            {\n                \"name\": \"product_name\",\n                \"type\": \"text\",\n                \"description\": \"商品名\",\n                \"classification\": \"public\",\n                \"index_hint\": \"fulltext+vector\",\n                \"required\": true\n            },\n            {\n                \"name\": \"price\",\n                \"type\": \"number\",\n                \"description\": \"価格（税込）\",\n                \"unit\": \"JPY\",\n                \"index_hint\": \"sort+facet\"\n            }\n        ]\n    }","operationId":"create_schema_api_v1_resources__resource_id__schema_post","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchemaCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SchemaResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources/{resource_id}/impact":{"get":{"tags":["resource-schema"],"summary":"Get Resource Impact","description":"Get resource change impact information.\n\nIssue #266: Shows the impact of creating/modifying a schema.\nOwner-only (#389): same two-layer gate as ``get_schema`` —\n``WorkspaceOwner`` → 403 for non-owners, ``resolve_resource_by_slug`` →\n404 on cross-workspace probes. The prior manual workspace-boundary\nSELECT block was superseded by the helper.\n\nExample:\n    GET /api/v1/resources/ec_products/impact","operationId":"get_resource_impact_api_v1_resources__resource_id__impact_get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceImpactResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources/{resource_id}/indexer-status":{"get":{"tags":["resource-indexer"],"summary":"Get Indexer Status","description":"Return indexer state and recent ingest events for a resource.\n\nThe ``resource_id`` URL path parameter accepts the human-readable slug\n(e.g. ``my-github-repo``), not an internal UUID. The slug is resolved to\nthe caller's workspace-scoped resource via\n``PermissionService.resolve_resource_by_slug``. This backward-compatible\ncontract is maintained so existing integrations continue to work without\nmodification after the v0.12.0 UUID FK migration.\n\nOwner-only (#389): ``WorkspaceOwner`` rejects non-owners with 403\n(``resource_token`` write-scoped credentials are rejected by the same\ndependency — pinned by isolation tests); ``resolve_resource_by_slug``\nreturns 404 (CWE-639 / OWASP A01) on cross-workspace probes so\nexistence does not leak.","operationId":"get_indexer_status_api_v1_resources__resource_id__indexer_status_get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IndexerStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resources":{"get":{"tags":["resources"],"summary":"List Resources","description":"List all resources in the caller's current workspace.\n\nReturns an ordered list (latest activity first) of contexts that have a\nnon-null ``resource_id``, with aggregated counts joined from the\nresource_tokens, memories, resource_schemas, and resource_events tables.\n\n#1863: a resource whose contexts were all deleted is still listed, with\nthe context fields null, while it has active tokens. Those tokens keep\nauthenticating ingest, so the owner needs a way to reach and revoke them;\nonce the last one is revoked the row disappears.\n\nOwner-only (#389): ``WorkspaceOwner`` rejects non-owners with 403.\n``get_accessible_contexts`` below is a no-op for owners and retained\nas defense-in-depth against a role-gate regression.\n\nExample:\n    GET /api/v1/resources","operationId":"list_resources_api_v1_resources_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resource-tokens":{"get":{"tags":["resource-tokens"],"summary":"List Resource Tokens","description":"List resource tokens with pagination (optionally filtered by resource_id).\n\nIssue #242: Owner-only access.\nIssue #264: Added pagination support.\nIssue #59: Changed from APIKeyOrSessionUser to WorkspaceOwner.\n\nArgs:\n    resource_id: Optional filter by resource_id\n    limit: Number of tokens per page (1-100, default 50)\n    offset: Starting offset (default 0)\n    owner: Workspace owner (user_id, workspace_id) from dependency\n    manager: ResourceTokenManager instance\n\nReturns:\n    Paginated response with tokens, total count, limit, and offset","operationId":"list_resource_tokens_api_v1_resource_tokens_get","parameters":[{"name":"resource_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by resource_id","title":"Resource Id"},"description":"Filter by resource_id"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Number of tokens per page (max 100)","default":50,"title":"Limit"},"description":"Number of tokens per page (max 100)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Starting offset for pagination","default":0,"title":"Offset"},"description":"Starting offset for pagination"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedResourceTokensResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["resource-tokens"],"summary":"Create Resource Token","description":"Create a new resource token.\n\nIssue #242: Owner-only access. Returns plaintext token ONLY once.\nIssue #276: Uses WorkspaceOwner dependency for DRY principle.\n\nThe token cap is the WORKSPACE's (#1919): the count of active,\nnon-connector tokens of the workspace — whoever minted them — must be\nunder the plan's ``max_resource_tokens``. It used to be counted per\ncreator, while the quota ceiling on PATCH was summed per workspace, so\ntwo owners could each mint a full set and leave the workspace with no\nquota raise that fitted. The population is the ceiling's\n(``auth.resource_tokens.workspace_regular_active_tokens``); the sum itself is not checked\nhere because count <= cap and quota <= 10000 per token already keep it\nat or under ``max_resource_tokens * 10000``.\n\nArgs:\n    data: Token creation request\n    owner: Workspace owner (user_id, workspace_id) from dependency\n    manager: ResourceTokenManager instance\n    db: Database session\n\nReturns:\n    Token metadata + plaintext token (shown ONLY once)\n\nRaises:\n    400: Invalid resource_id\n    403: Not workspace owner, plan without the feature, or the workspace\n        is at its token cap (``QUOTA-001``)\n    500: Failed to create token","operationId":"create_resource_token_api_v1_resource_tokens_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceTokenCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceTokenCreateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/resource-tokens/{token_id}":{"patch":{"tags":["resource-tokens"],"summary":"Update Resource Token","description":"Update resource token description and/or quota.\n\nIssue #242: Allow updating token metadata without regenerating.\nIssue #276: Uses WorkspaceOwner dependency for DRY principle.\n\nThe token is the WORKSPACE's to update (#1863): any owner may relabel any\ntoken of the workspace, whoever minted it. It is resolved with the same\npredicate the list and the quota ceiling use (#1919,\n``auth.resource_tokens.workspace_tokens`` — the ``resources`` row its\n``resource_pk`` points at, or its own\n``workspace_id`` for a legacy row without one), so a token the ceiling\ncounts is always addressable; it used to be matched on the shadow\n``workspace_id`` column alone and was a 404 when that was never\nbackfilled. A quota raise is checked against the workspace's ceiling,\n``max_resource_tokens * 10000`` events/hour over the workspace's active\nnon-connector tokens; a lowering is never refused.\n\nArgs:\n    token_id: Public id of the token (``rtok_...``)\n    request: Update request\n    owner: Workspace owner (user_id, workspace_id) from dependency\n    db: Database session\n\nReturns:\n    Updated token metadata\n\nRaises:\n    400: The quota raise does not fit the workspace's ceiling\n    403: Not workspace owner\n    404: Token not found (or not this workspace's — the same answer)","operationId":"update_resource_token_api_v1_resource_tokens__token_id__patch","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","pattern":"^rtok_[0-9A-Za-z]{22}$","description":"Public id of the resource token (`rtok_` + 22 base62 characters).","examples":["rtok_0000000000000000000000"],"title":"Token Id"},"description":"Public id of the resource token (`rtok_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceTokenUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceTokenResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["resource-tokens"],"summary":"Revoke Resource Token","description":"Revoke a resource token (soft delete).\n\nIssue #242: Owner-only access. Sets is_active=False (preserves audit trail).\nIssue #276: Uses WorkspaceOwner dependency for DRY principle.\n\nThe token is the WORKSPACE's to revoke (#1863): any owner may revoke any\ntoken of the workspace, including one minted by a departed member or a\nconnector. It is resolved like the list, the update route and the quota\nceiling (#1919, ``auth.resource_tokens.workspace_tokens``): by the ``resources`` row its\n``resource_pk`` points at — so a token whose contexts were deleted, or\nwhose shadow ``workspace_id`` was never backfilled, can still be revoked\nwhile it still authenticates ingest.\n\nArgs:\n    token_id: Public id of the token to revoke (``rtok_...``)\n    owner: Workspace owner (user_id, workspace_id) from dependency\n    manager: ResourceTokenManager instance\n    db: Database session\n\nRaises:\n    403: Not workspace owner\n    404: Token not found (or not this workspace's — the same answer)\n    500: Failed to revoke token","operationId":"revoke_resource_token_api_v1_resource_tokens__token_id__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","pattern":"^rtok_[0-9A-Za-z]{22}$","description":"Public id of the resource token (`rtok_` + 22 base62 characters).","examples":["rtok_0000000000000000000000"],"title":"Token Id"},"description":"Public id of the resource token (`rtok_` + 22 base62 characters)."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors":{"post":{"tags":["workspace-connectors"],"summary":"Create Workspace Connector","description":"Provision a connector using the Resource Foundation.\n\nRequires a plan with the ``connectors`` feature (XL, #1551); existing\nconnectors on lower plans keep working. The ``max_connectors`` seat cap\napplies second, inside ``ConnectorProvisioningService``.","operationId":"create_workspace_connector_api_v1_workspace_connectors_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorCreateRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorCreateResponse"}}}},"409":{"description":"`RES-002`. For a platform team that is already connected, `details.reason` says where (#1753): `connector_team_connected_here` (this workspace; `details.connector_id` and `details.display_name` name the existing connector — edit it instead; `details.token_refreshed: true` is added when the request carried a Slack install handle and that install's bot token was stored on the existing connector, #1880) or `connector_team_connected_elsewhere` (another workspace; no other details). A `resource_id` clash is a 409 without `reason`.","content":{"application/json":{"example":{"error":"RES-002","message":"This slack team is already connected to another workspace. A team can be connected to one workspace only.","details":{"reason":"connector_team_connected_elsewhere"}}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["workspace-connectors"],"summary":"List Workspace Connectors","description":"List connectors for the current workspace (workspace-admin scoped).","operationId":"list_workspace_connectors_api_v1_workspace_connectors_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/WorkspaceConnectorSummary"},"title":"Response List Workspace Connectors Api V1 Workspace Connectors Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors/{connector_id}/runtime":{"patch":{"tags":["workspace-connectors"],"summary":"Update Workspace Connector Runtime","description":"Replace per-connector runtime controls (workspace-admin scoped).","operationId":"update_workspace_connector_runtime_api_v1_workspace_connectors__connector_id__runtime_patch","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorRuntimeUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorRuntimeUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors/{connector_id}":{"patch":{"tags":["workspace-connectors"],"summary":"Update Workspace Connector Settings","description":"PATCH connector vend settings (#1376): channel_ids / LLM binding / locale.\n\nRepairs connectors born un-vendable (the create endpoint was previously\nthe only writer of these columns). True PATCH semantics — absent fields\nare untouched, explicit ``null`` clears — with the runtime PATCH's\n``expected_config_version`` optimistic lock (409 on staleness).","operationId":"update_workspace_connector_settings_api_v1_workspace_connectors__connector_id__patch","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorSettingsUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceConnectorSettingsUpdateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["workspace-connectors"],"summary":"Delete Workspace Connector","description":"Delete a connector (workspace-admin scoped).\n\nRevokes the connector's KMC write key and drops its resource so the worker\nstops on its next config fetch. 404 if the connector is not in the workspace.","operationId":"delete_workspace_connector_api_v1_workspace_connectors__connector_id__delete","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors/available-apps":{"get":{"tags":["workspace-connectors"],"summary":"List Available Worker Apps","description":"List active, non-secret app identities available for connector binding.","operationId":"list_available_worker_apps_api_v1_workspace_connectors_available_apps_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/AvailableWorkerApp"},"title":"Response List Available Worker Apps Api V1 Workspace Connectors Available Apps Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors/{connector_id}/channels":{"get":{"tags":["workspace-connectors"],"summary":"List Connector Channels","description":"List a Slack connector's channels for the settings picker (#1391, #1778).\n\nServer-side proxy of Slack ``conversations.list`` using the connector's\nFernet-decrypted bot token — the token never reaches the browser. Read-only\nand workspace-admin scoped; a cross-workspace / unknown connector is a\nuniform 404. The optional ``q`` filters the fetched page by name\n(case-insensitive substring); Slack's API has no name filter.\n\nPublic channels and the private channels the bot is a member of are\nlisted (#1778). An install granted before ``groups:read`` was requested\nstill gets its public listing, with ``missing_scopes=[\"groups:read\"]`` so\nthe picker can explain the gap and suggest reconnecting.\n\nErrors degrade gracefully to the manual-ID entry lane rather than 5xx:\na token missing the ``channels:read`` scope (legacy installs) is a 409\n``CONNECTOR-SCOPE``; Slack rate-limiting is a 429 with ``Retry-After``.","operationId":"list_connector_channels_api_v1_workspace_connectors__connector_id__channels_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Cursor"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Q"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorChannelsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workspace-connectors/{connector_id}/rotate-kmc-key":{"post":{"tags":["workspace-connectors"],"summary":"Rotate Connector Kmc Key","description":"Rotate the KMC write key for a connector (workspace-admin scoped).\n\nRevokes the current key immediately, mints a replacement with a 365-day\nexpiry, and bumps ``config_version`` so the worker re-fetches on its next\npoll. The new plaintext key is returned exactly once.\n\n**No grace period (v1)**: coordinate rotation during a maintenance window\nor a brief pause in ai-worker activity; the old key is invalid as soon as\nthis call returns. A grace-period dual-key approach is deferred to a\nfollow-up issue once usage patterns are better understood.\n\nIssue #1752: the key's owner is emailed a security notice after the\ncommit, naming the rotating admin when that is someone else.","operationId":"rotate_connector_kmc_key_api_v1_workspace_connectors__connector_id__rotate_kmc_key_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateKmcKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workers/apps":{"get":{"tags":["workers"],"summary":"Get Worker Apps","description":"Bootstrap app identities and signing secrets over the internal lane.\n\nThis service token grants no memory authority. Disabled/unconfigured apps\nremain in the list without secret material so a polling worker can evict\nstale verification state deterministically.","operationId":"get_worker_apps_api_v1_workers_apps_get","parameters":[{"name":"If-None-Match","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"If-None-Match"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppBootstrapResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workers/config":{"get":{"tags":["workers"],"summary":"Get Worker Config","description":"Return the connector config for a platform team (worker dispatch).\n\n404 when no connector serves the team, the connector has no write-target\ncontext yet (registration incomplete), its workspace's plan has connectors\nsuspended (#1939), it has no KMC write key, or its\n``llm_config`` cannot vend (#1447) — the worker treats all of them as\nnot-ready and skips the team rather than failing the dispatch.","operationId":"get_worker_config_api_v1_workers_config_get","parameters":[{"name":"platform","in":"query","required":true,"schema":{"const":"slack","type":"string","title":"Platform"}},{"name":"team_id","in":"query","required":true,"schema":{"type":"string","maxLength":255,"title":"Team Id"}},{"name":"app_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$"},{"type":"null"}],"title":"App Key"}},{"name":"If-None-Match","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"If-None-Match"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerConnectorConfig"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/workers/active-color":{"get":{"tags":["workers"],"summary":"Live blue-green deploy color","description":"Report the color serving traffic, read from the deploy marker per request.\n\nA co-resident consumer of this lane used to learn the live color by\nbind-mounting the marker file itself, which pins the inode at container\nstart and silently goes stale (#1482). Here the process that owns the\nmarker reads it on every call, so the answer is always the marker's\ncurrent value. ``responding_color`` is the identity of the instance that\nanswered: when it differs from ``active_color`` the caller has reached a\ncolor that is no longer live and should re-target.","operationId":"get_active_color_api_v1_workers_active_color_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerActiveColorResponse"}}}},"503":{"description":"The deploy marker is missing, unreadable or not a known color (error code DEPLOY-001, ``details.reason`` = missing | unreadable | invalid). No color is guessed. Every reason can be transient while the marker is republished during a color switch — treat the 503 as retryable."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/worker-apps":{"get":{"tags":["admin-worker-apps"],"summary":"List Worker Apps","operationId":"list_worker_apps_api_v1_admin_worker_apps_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/WorkerAppAdminResponse"},"type":"array","title":"Response List Worker Apps Api V1 Admin Worker Apps Get"}}}}}},"post":{"tags":["admin-worker-apps"],"summary":"Create Worker App","operationId":"create_worker_app_api_v1_admin_worker_apps_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppCreateRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppAdminResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/worker-apps/{platform}/{app_key}":{"patch":{"tags":["admin-worker-apps"],"summary":"Update Worker App","operationId":"update_worker_app_api_v1_admin_worker_apps__platform___app_key__patch","parameters":[{"name":"platform","in":"path","required":true,"schema":{"enum":["slack","discord","teams"],"type":"string","title":"Platform"}},{"name":"app_key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"App Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppUpdateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppAdminResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/admin/worker-apps/{platform}/{app_key}/rotate-secret":{"post":{"tags":["admin-worker-apps"],"summary":"Rotate Worker App Secret","operationId":"rotate_worker_app_secret_api_v1_admin_worker_apps__platform___app_key__rotate_secret_post","parameters":[{"name":"platform","in":"path","required":true,"schema":{"enum":["slack","discord","teams"],"type":"string","title":"Platform"}},{"name":"app_key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"App Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppRotateSecretRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkerAppAdminResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/workspaces/{workspace_id}/plan":{"put":{"tags":["internal-billing"],"summary":"Set Workspace Plan From Billing","description":"Set a workspace's entitlement (plan tier + addon quota) from billing.\n\nIdempotent, service-authenticated, internal-only. Sets ``plan_name``; when\n``addons`` is provided it is applied as a FULL REPLACE (dimensions omitted\nfrom the map reset to 0) so a tier change cannot strand a prior tier's addon\nbonus; omit ``addons`` to leave them unchanged. Does not perform destructive\ndowngrade cascades (see module docstring). Returns the full applied addon state.","operationId":"set_workspace_plan_from_billing_internal_workspaces__workspace_id__plan_put","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPlanPush"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPlanPushResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["internal-billing"],"summary":"Get Workspace Entitlement","description":"Read a workspace's current entitlement + ``entitlement_source`` (#1095).\n\nService-authenticated, internal-only. The external reconciler reads this to\ndecide whether to reconcile a workspace (``external_billing``) or leave it\nuntouched (``admin_grant``) — so a periodic billing reconcile never reverts a\nlocally-owned admin/comp grant.","operationId":"get_workspace_entitlement_internal_workspaces__workspace_id__plan_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceEntitlementView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/workspaces/{workspace_id}/owner":{"get":{"tags":["internal-billing"],"summary":"Get Workspace Owner Check","description":"Re-check whether ``sub`` currently owns a workspace (#2181).\n\nService-authenticated, internal-only, read-only. The billing service calls\nthis before a billing action so a user who has lost ownership since their\nbilling session started (transfer, demotion, erasure) cannot act on the\nworkspace's subscription.\n\n``is_owner`` is strict equality against the canonical owner pointer\n``Workspace.owner_user_id`` (moved only by the ownership service and\nerasure). Identity links are not consulted (they share private contexts,\nnot workspace roles) and there is no membership-table fallback.\n\n404 is returned ONLY when no workspace row has this id. A soft-deleted\nworkspace answers 200 ``is_owner: false`` — a deliberate deviation from the\nentitlement read, which 404s on it: the billing service treats a literal\n404 as \"this route is not deployed\" and then falls back to its own session,\nso an existing-but-deleted workspace must fail closed with ``false``\ninstead. A workspace with no owner pointer also answers ``false``. A row\nthat is gone entirely (hard-deleted) answers 404 by the agreed contract.\n\nRaises:\n    ValidationError: ``workspace_id`` is not a UUID or ``sub`` is blank (422).\n    NotFoundException: no workspace row has this id (404).","operationId":"get_workspace_owner_check_internal_workspaces__workspace_id__owner_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"sub","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":255,"description":"User id (``users.user_id``) whose ownership is re-checked","title":"Sub"},"description":"User id (``users.user_id``) whose ownership is re-checked"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceOwnerView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/workspaces/{workspace_id}/downgrade-eligibility":{"get":{"tags":["internal-billing"],"summary":"Get Downgrade Eligibility","description":"Report whether a workspace's usage fits each lower tier (#1123).\n\nService-authenticated, internal-only. memory-cloud is the usage SoT; the\nexternal billing service gates its portal downgrade UI on this so it never\noffers a downgrade that current usage cannot satisfy (purchased addons are\nkept — the fit is against the target tier base + retained addon bonuses).\nThe absolute ``PUT .../plan`` push stays reconcile-safe and does NOT itself\nenforce this guard; enforcement is the portal's responsibility, informed by\nthis read. Soft-deleted workspaces 404 (consistent with the entitlement read).","operationId":"get_downgrade_eligibility_internal_workspaces__workspace_id__downgrade_eligibility_get","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DowngradeEligibilityView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/internal/workspaces/{workspace_id}/capacity-lock-notice":{"post":{"tags":["internal-billing"],"summary":"Send Capacity Lock Notice","description":"Warn the owner before a paid period ends that Free would lock the workspace (#1941).\n\nmemory-cloud does not store ``current_period_end``, so the billing service\ndecides WHEN (e.g. 7 days before the end of a cancelled subscription) and\ncalls this. memory-cloud evaluates the projected Free state with the same\ncounting as the capacity lock — Free tier limits plus the bonuses a\ndowngrade keeps, files of deleted contexts excluded — and, when over,\nemails the workspace owner (plain text, no memory content).\n\nIdempotent per ``(workspace, period_end)``: a repeat answers\n``sent=false, reason=\"already_sent\"``. In addition, at most one notice per\nworkspace per 24 hours (``reason=\"cooldown\"``). A failed delivery releases\nboth claims so a retry can send. 404 for a missing or soft-deleted workspace.","operationId":"send_capacity_lock_notice_internal_workspaces__workspace_id__capacity_lock_notice_post","parameters":[{"name":"workspace_id","in":"path","required":true,"schema":{"type":"string","title":"Workspace Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityLockNoticeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CapacityLockNoticeResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/billing/handoff":{"post":{"tags":["billing-handoff"],"summary":"Mint Billing Handoff","description":"Mint an owner-scoped, short-lived billing handoff token.\n\nOwner-only + session-only. Returns 403 for admin/member/API-key callers and\n503 when the signing key is unconfigured (fail-closed).\n\nThe token's ``iss`` / ``aud`` claims (``billing_handoff_issuer`` /\n``billing_handoff_audience``) are FROZEN cross-repo JWT contract values\nverified by the external billing service — they are NOT repo or service\nnames. ``billing`` is the function; the external service provides it.\nChanging either claim is a coordinated breaking change (the verifier must\nchange in lockstep), never a local rename.","operationId":"mint_billing_handoff_api_v1_billing_handoff_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingHandoffRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingHandoffResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/connectors/slack/install":{"get":{"tags":["connectors"],"summary":"Slack Install","description":"Begin the Slack OAuth install for the current workspace.","operationId":"slack_install_api_v1_connectors_slack_install_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/connectors/slack/callback":{"get":{"tags":["connectors"],"summary":"Slack Callback","description":"Handle the Slack OAuth callback: validate state, exchange code, stash install.","operationId":"slack_callback_api_v1_connectors_slack_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"}],"description":"OAuth2 error code (e.g. access_denied) when the user cancels","title":"Error"},"description":"OAuth2 error code (e.g. access_denied) when the user cancels"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable OAuth2 error detail (not reflected)","title":"Error Description"},"description":"Human-readable OAuth2 error detail (not reflected)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/connectors/slack/pending/{handle}":{"get":{"tags":["connectors"],"summary":"Slack Pending","description":"Return the non-secret summary of a pending Slack install (for the form).\n\nPeeks WITHOUT consuming (the install is popped at connector-create time) and\nnever exposes the bot token. 404 if the handle is unknown/expired or belongs\nto another workspace.","operationId":"slack_pending_api_v1_connectors_slack_pending__handle__get","parameters":[{"name":"handle","in":"path","required":true,"schema":{"type":"string","title":"Handle"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Slack Pending Api V1 Connectors Slack Pending  Handle  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/{context_id}/search":{"post":{"tags":["public-search"],"summary":"Public Search","description":"Search public context with schema-aware response formatting.\n\nIssue #238: Public REST API for external systems (EC inventory, docs, etc.).\nIssue #626: Optional API-key attribution via ``Authorization: Bearer\n<kagura_…>``. A bound key gets a per-key rate-limit bucket and audit\nattribution; an unbound key (owner / workspace-scoped) is rejected so\ncallers don't accidentally route their primary credentials through a\npublic path. Anonymous access continues to work without any header.\n\nAuthentication:\n    - Workspace member session (authenticated access, no rate limit)\n    - Public-bound API key (#626) — per-key rate limit, audit-attributed\n    - Anonymous access (rate limited to 50 req/min per context, shared)\n\nRate Limits:\n    - Anonymous access: 50 requests/minute per context (shared bucket)\n    - Public-bound key: per-key bucket from workspace plan tier\n      (``bound_public_calls_per_minute``)\n    - Workspace members: No limit (uses normal workspace quota)\n\nRequest:\n    POST /api/v1/public/{context_id}/search\n    Headers:\n        Authorization: Bearer kagura_…    (optional; required only for\n                                           attributed/bound-key access)\n    Body:\n    {\n        \"query\": \"商品名 価格\",\n        \"limit\": 10,\n        \"use_rerank\": false\n    }\n\nResponse:\n    {\n        \"status\": \"success\",\n        \"query\": \"商品名 価格\",\n        \"results\": [\n            {\n                \"memory_id\": \"...\",\n                \"content\": \"商品名: ..., 価格: ...\",\n                \"score\": 0.95,\n                \"metadata\": {...},\n                \"highlighted\": {...}\n            }\n        ],\n        \"count\": 5,\n        \"context_id\": \"...\",\n        \"resource_id\": \"ec_products\",\n        \"schema_version\": 1\n    }\n\nErrors:\n    - 401: API key supplied but invalid / expired\n    - 403: Context is not public, OR API key is not public-bound, OR\n           API key is bound to a different context (CWE-639 IDOR)\n    - 404: Context not found\n    - 429: Rate limit exceeded (anonymous or per-key bucket), or the\n           workspace's plan has no public API allowance (#1939)","operationId":"public_search_api_v1_public__context_id__search_post","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSearchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PublicSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/public/{context_id}/info":{"get":{"tags":["public-search"],"summary":"Get Public Context Info","description":"Get public context metadata and schema information.\n\nReturns basic information about the public context including:\n- Context name and description\n- Resource schema (if resource-backed)\n- Statistics (memory count, last updated)\n\nIssue #626: An optional ``Authorization: Bearer <api_key>`` is accepted\nfor symmetry with ``/search``. The same IDOR guard applies — a key\nbound to a different context is rejected with 403 — but no rate limit\nor audit attribution is applied here because ``/info`` returns only\nstatic metadata (no memory contents).\n\nArgs:\n    context_id: Context UUID\n    api_key: Optional public-bound API key (Issue #626).\n    db: Database session\n\nReturns:\n    Public context metadata\n\nErrors:\n    - 401: API key supplied but invalid / expired\n    - 403: Context is not public, OR API key is not public-bound, OR\n           API key is bound to a different context (CWE-639 IDOR)\n    - 404: Context not found\n    - 429: The workspace's plan has no public API allowance (#1939)","operationId":"get_public_context_info_api_v1_public__context_id__info_get","parameters":[{"name":"context_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Context Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/stats":{"get":{"tags":["graph"],"summary":"Get Graph Stats","description":"Get Neural Memory graph statistics for specified context.\n\nIssue #82: Context-scoped stats.\nIssue #383: Authorization is delegated to ``PermissionService``. Previously\nthis endpoint hard-filtered by ``user_id == caller``, hiding shared-context\nmemories authored by other workspace members. Now:\n\n    - Shared context (``is_private = false``): any workspace member sees\n      the full graph for the context.\n    - Private context (``is_private = true``): only the private-context\n      creator can access the graph for that context. The creator-only\n      rule matches ``can_access_memory`` (returns ``False`` for\n      non-creators) and ``check_context_access`` (raises 403 for\n      non-creators). This endpoint deliberately **remaps that denial\n      to 404**, unifying with the cross-workspace-probe shape so\n      neither path leaks existence — an intentional divergence from\n      ``check_context_access``'s 403 status code, not from its\n      authorization semantics.\n    - Cross-workspace probes and private-context access by non-creators\n      both surface as 404 (CWE-639 / OWASP A01 uniform disclosure)\n      to avoid existence leakage.\n\nArgs:\n    context_id: Optional context ID. When absent, returns an empty graph\n        (no scope = no visible edges).\n\nReturns:\n    Graph statistics including node/edge counts, top connections, etc.","operationId":"get_graph_stats_api_v1_graph_stats_get","parameters":[{"name":"context_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Optional context ID. When absent, returns an empty graph (no scope = no visible edges) per Issue #383.","title":"Context Id"},"description":"Optional context ID. When absent, returns an empty graph (no scope = no visible edges) per Issue #383."},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphStatsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/data":{"get":{"tags":["graph"],"summary":"Get Graph Data","description":"Get Neural Memory graph data for visualization (context-scoped).\n\nArgs:\n    context_id: Context ID for scoping (required for proper isolation)\n    limit_nodes: Maximum nodes to return (default: 100, max: 500)\n    min_weight: Minimum edge weight threshold\n    memory_types: Optional list of memory types to include\n\nReturns:\n    Graph data with nodes and edges","operationId":"get_graph_data_api_v1_graph_data_get","parameters":[{"name":"context_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Context ID (required for isolation)","title":"Context Id"},"description":"Context ID (required for isolation)"},{"name":"limit_nodes","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Maximum number of nodes to return","default":100,"title":"Limit Nodes"},"description":"Maximum number of nodes to return"},{"name":"min_weight","in":"query","required":false,"schema":{"type":"number","maximum":3.0,"minimum":0.0,"description":"Minimum edge weight to include","default":0.0,"title":"Min Weight"},"description":"Minimum edge weight to include"},{"name":"memory_types","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter by memory types (e.g., code, note)","title":"Memory Types"},"description":"Filter by memory types (e.g., code, note)"},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GraphDataResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/graph/edges":{"post":{"tags":["graph"],"summary":"Create Graph Edge","description":"Create a user-declared edge between two memories in a context (#1416).\n\nREST twin of the MCP ``create_edge`` tool: both call the shared\n``services.edge_service.create_declared_edge`` so the deterministic\ndeclared-duplicate contract (#1321) and the #1403 supersede accept/self-heal\nbehave identically. The web UI's confirm→create flow for a supersede\nsuggestion posts here with ``edge_type=\"supersedes\"``; accepting a stored\nsuggestion clears the ``supersede_candidate`` in the same transaction.\n\nAuthorization: mirrors the MCP ``create_edge`` tool and the REST\n``remember`` / ``patch`` writers — workspace membership at ``member`` level\nor higher (viewers are read-only). Resolution goes through\n``resolve_context_for_workspace_read``, which returns a **uniform 404** on\nevery deny path (context not found, caller not a workspace member, viewer,\nprivate-context non-creator, whitelist/agent-binding miss) so context\nexistence never leaks across workspace/privacy boundaries (CWE-639 / OWASP\nA01). Both endpoints must be live memories in the resolved (workspace,\ncontext) — a missing/out-of-scope/deleted endpoint is a 404, which also\ncloses the edge write-time invariant the ``/graph`` read paths defend\nagainst.\n\nStatus: 201 when a new edge is created; 200 for updated / unchanged; 409\nwhen a differing declared edge already exists (unless ``overwrite=true``).","operationId":"create_graph_edge_api_v1_graph_edges_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEdgeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/system/health":{"get":{"tags":["system"],"summary":"Health Check","description":"Health check endpoint.\n\nReturns:\n    Status OK\n\nExample:\n    GET /api/v1/health\n    -> {\"status\": \"ok\"}","operationId":"health_check_api_v1_system_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/info":{"get":{"tags":["system"],"summary":"System Info","description":"System information endpoint.\n\nReturns:\n    System info with version, environment, features\n\nExample:\n    GET /api/v1/system/info\n    -> {\n        \"name\": \"Kagura Memory Cloud\",\n        \"version\": \"0.1.0\",\n        \"environment\": \"development\",\n        \"features\": {...}\n    }","operationId":"system_info_api_v1_system_info_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/telemetry":{"get":{"tags":["system"],"summary":"Get System Telemetry","description":"Get system telemetry and health status.\n\nChecks connectivity and status of all backend services.\n\nArgs:\n    user: Authenticated user\n    db: Database session\n\nReturns:\n    System telemetry including service status, memory stats, and uptime","operationId":"get_system_telemetry_api_v1_system_telemetry_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/system/overview":{"get":{"tags":["system"],"summary":"Get System Overview","description":"Get comprehensive system overview.\n\nReturns database, vector DB, cache, graph, and neural memory status.\nIssue #45 - Replace mock API with real implementation.\n\nReturns:\n    Comprehensive system overview including all backends","operationId":"get_system_overview_api_v1_system_overview_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/v1/system/embedding/models":{"get":{"tags":["system"],"summary":"List Embedding Models","description":"List available embedding models with availability status.\n\nReturns all models from EMBEDDING_MODEL_REGISTRY with availability\nbased on whether the provider is configured and reachable.\n\nReturns:\n    List of models with name, dimensions, provider, and availability","operationId":"list_embedding_models_api_v1_system_embedding_models_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingModelsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"tags":["well-known"],"summary":"Oauth Protected Resource Mcp","description":"OAuth 2.0 Protected Resource Metadata for /mcp endpoint.\n\nClaude Desktop may request resource-specific metadata.\nRedirects to main oauth-protected-resource endpoint.","operationId":"oauth_protected_resource_mcp__well_known_oauth_protected_resource_mcp_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-protected-resource":{"get":{"tags":["well-known"],"summary":"Oauth Protected Resource","description":"OAuth 2.0 Protected Resource Metadata.\n\nRequired by OpenAI Apps SDK for ChatGPT integration.\n\nSpec: RFC 8707 - Resource Indicators for OAuth 2.0\nhttps://datatracker.ietf.workspace/doc/html/rfc8707\n\nReturns:\n    Protected resource metadata","operationId":"oauth_protected_resource__well_known_oauth_protected_resource_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/openid-configuration":{"get":{"tags":["well-known"],"summary":"Openid Configuration","description":"OpenID Connect Discovery 1.0 Metadata.\n\nRequired by MCP clients (Claude Desktop, Claude Code) per MCP Authorization\nspecification (June 2025 revision).\n\nSpec: OpenID Connect Discovery 1.0\nhttps://openid.net/specs/openid-connect-discovery-1_0.html\n\nMCP Authorization Spec:\nhttps://modelcontextprotocol.io/specification/draft/basic/authorization\n\nReturns:\n    OpenID Connect Discovery metadata with PKCE support (required by MCP)\n\nNote:\n    This endpoint is functionally equivalent to /oauth-authorization-server\n    but uses OpenID Connect Discovery format, which many authorization servers\n    (Azure AD, Auth0, etc.) prefer. MCP clients check this endpoint first,\n    then fall back to /oauth-authorization-server if not found.\n\n    CRITICAL: code_challenge_methods_supported MUST be present, or MCP clients\n    will refuse to proceed (MCP spec requirement).","operationId":"openid_configuration__well_known_openid_configuration_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/.well-known/oauth-authorization-server":{"get":{"tags":["well-known"],"summary":"Oauth Authorization Server","description":"OAuth 2.0 Authorization Server Metadata.\n\nProvides discovery information about the authorization server.\n\nSpec: RFC 8414 - OAuth 2.0 Authorization Server Metadata\nhttps://datatracker.ietf.workspace/doc/html/rfc8414\n\nThis server implements its own OAuth2 Authorization Server (Issue #33).\nFor MCP clients like Claude Desktop, use API Key authentication instead.\n\nReturns:\n    Authorization server metadata","operationId":"oauth_authorization_server__well_known_oauth_authorization_server_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/":{"get":{"tags":["system"],"summary":"Root","description":"Root endpoint.","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"tags":["system"],"summary":"Health","description":"Health check endpoint (liveness probe — always fast).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/readiness":{"get":{"tags":["system"],"summary":"Readiness","description":"Readiness probe for blue-green deploy.\n\nChecks PostgreSQL, Qdrant, and Redis connectivity with tight timeouts.\nReturns 200 only when all backends are reachable.\nReturns 503 if any backend is unavailable.\n\nNote: Uses _get_session_factory() directly (not Depends(get_db)) because\nthis is a standalone probe, not a typical route handler.","operationId":"readiness_readiness_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"APIKeyCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","description":"Friendly name for the API key"},"expires_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":0.0},{"type":"null"}],"title":"Expires Days","description":"Expiration in days (1-3650). Omit for the server default (365 days); 0 opts in to a key that never expires."},"default_context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default Context Id","description":"MCP default context of the key: a context-scoped tool called without context_id runs in it (?context= on the URL wins). Omit for none."}},"type":"object","required":["name"],"title":"APIKeyCreate","description":"Request model for creating an API key."},"APIKeyCreateResponse":{"properties":{"id":{"title":"Id","description":"Public id of the key (`akey_` + 22 base62 characters)"},"key_prefix":{"title":"Key Prefix","description":"First 16 characters of key (for display)"},"name":{"title":"Name","description":"Friendly name"},"user_id":{"title":"User Id","description":"Owner user ID"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"revoked_at":{"title":"Revoked At","description":"Revocation timestamp"},"expires_at":{"title":"Expires At","description":"Expiration timestamp"},"status":{"title":"Status","description":"Current status"},"default_context_id":{"title":"Default Context Id","description":"The key's MCP default context (#2129); null when none."},"default_context_name":{"title":"Default Context Name","description":"Name of the default context; null when none is set or the context is no longer readable by the key's owner."},"api_key":{"title":"Api Key","description":"Plaintext API key (ONLY shown once - must be saved by client)"}},"type":"object","required":["id","key_prefix","name","user_id","created_at","status","api_key"],"title":"APIKeyCreateResponse","description":"Response model for API key creation (includes plaintext key)."},"APIKeyResponse":{"properties":{"id":{"title":"Id","description":"Public id of the key (`akey_` + 22 base62 characters)"},"key_prefix":{"title":"Key Prefix","description":"First 16 characters of key (for display)"},"name":{"title":"Name","description":"Friendly name"},"user_id":{"title":"User Id","description":"Owner user ID"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"revoked_at":{"title":"Revoked At","description":"Revocation timestamp"},"expires_at":{"title":"Expires At","description":"Expiration timestamp"},"status":{"title":"Status","description":"Current status"},"default_context_id":{"title":"Default Context Id","description":"The key's MCP default context (#2129); null when none."},"default_context_name":{"title":"Default Context Name","description":"Name of the default context; null when none is set or the context is no longer readable by the key's owner."}},"type":"object","required":["id","key_prefix","name","user_id","created_at","status"],"title":"APIKeyResponse","description":"Response model for API key metadata."},"APIKeyStats":{"properties":{"total_requests":{"type":"integer","minimum":0.0,"title":"Total Requests","description":"Total requests in period"},"daily_stats":{"items":{"$ref":"#/components/schemas/DailyStats"},"type":"array","title":"Daily Stats","description":"Daily breakdown"},"period_start":{"type":"string","title":"Period Start","description":"Start date of statistics period"},"period_end":{"type":"string","title":"Period End","description":"End date of statistics period"}},"type":"object","required":["total_requests","daily_stats","period_start","period_end"],"title":"APIKeyStats","description":"API key usage statistics."},"APIKeyUpdate":{"properties":{"default_context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default Context Id","description":"The key's MCP default context; null clears it."}},"type":"object","required":["default_context_id"],"title":"APIKeyUpdate","description":"Request model for ``PATCH /config/api-keys/{key_id}`` (#2129)."},"AcceptInvitationRequest":{"properties":{"token":{"type":"string","minLength":20,"title":"Token","description":"Invitation token"}},"type":"object","required":["token"],"title":"AcceptInvitationRequest","description":"Accept invitation request.\n\nIssue #165: Team Collaboration - Workspace Invitation System"},"AcceptInvitationResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"workspace":{"additionalProperties":true,"type":"object","title":"Workspace"},"member":{"additionalProperties":true,"type":"object","title":"Member"}},"type":"object","required":["success","workspace","member"],"title":"AcceptInvitationResponse","description":"Accept invitation response.\n\nIssue #165: Team Collaboration - Workspace Invitation System"},"AccountSwitchRequest":{"properties":{"user_id":{"type":"string","maxLength":255,"minLength":1,"title":"User Id"}},"type":"object","required":["user_id"],"title":"AccountSwitchRequest","description":"Which already-signed-in account to make active (#1488)."},"AddContextMemberRequest":{"properties":{"user_id":{"type":"string","minLength":1,"title":"User Id"},"role":{"type":"string","pattern":"^(owner|editor|viewer)$","title":"Role"}},"type":"object","required":["user_id","role"],"title":"AddContextMemberRequest","description":"Request model for adding context member."},"AddMemberRequest":{"properties":{"user_id":{"type":"string","minLength":1,"title":"User Id"},"role":{"type":"string","pattern":"^(owner|admin|member|viewer)$","title":"Role"}},"type":"object","required":["user_id","role"],"title":"AddMemberRequest","description":"Request model for adding member."},"AddonCacheDriftEntry":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"workspace_name":{"type":"string","title":"Workspace Name"},"addon_type":{"type":"string","title":"Addon Type"},"cache_column":{"type":"string","title":"Cache Column"},"cache_value":{"type":"integer","title":"Cache Value"},"expected_value":{"type":"integer","title":"Expected Value"}},"type":"object","required":["workspace_id","workspace_name","addon_type","cache_column","cache_value","expected_value"],"title":"AddonCacheDriftEntry","description":"One drifted addon cache column for a workspace (Issue #799).\n\nReports a workspace whose cached ``addon_*_bonus`` column disagrees with\nthe row-based SSoT (``SUM(active WorkspaceAddon.quantity) × unit_value``).\nA healthy system returns an empty list; the ``e23_799`` normalization\nmigration brings the live count to 0."},"AddonValues":{"properties":{"memory_bonus":{"type":"integer","title":"Memory Bonus","default":0},"mcp_quota_bonus":{"type":"integer","title":"Mcp Quota Bonus","default":0},"rest_quota_bonus":{"type":"integer","title":"Rest Quota Bonus","default":0},"public_quota_bonus":{"type":"integer","title":"Public Quota Bonus","default":0},"member_bonus":{"type":"integer","title":"Member Bonus","default":0},"context_bonus":{"type":"integer","title":"Context Bonus","default":0},"analysis_bonus":{"type":"integer","title":"Analysis Bonus","default":0},"storage_bonus_mb":{"type":"integer","title":"Storage Bonus Mb","default":0},"sleep_contexts_bonus":{"type":"integer","title":"Sleep Contexts Bonus","default":0},"connector_bonus":{"type":"integer","title":"Connector Bonus","default":0},"memories_per_day_bonus":{"type":"integer","title":"Memories Per Day Bonus","default":0}},"type":"object","title":"AddonValues","description":"Addon bonus values.\n\nIssue #665: Extended to expose all 9 addon cache columns so the admin\nUI can read back any value the PUT handler accepts. Pre-#665 only the\n5 that were directly writable from the legacy handler were surfaced."},"AdminErasureRequestBody":{"properties":{"reason_code":{"type":"string","enum":["user_request_via_support","legal_order","inactivity_policy","abuse_violation","other"],"title":"Reason Code"},"reason_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason Detail"}},"type":"object","required":["reason_code"],"title":"AdminErasureRequestBody","description":"Payload for POST /admin/users/{user_id}/erase.\n\n``reason_detail`` is free-form admin notes bounded to 1000 chars at\nthe service layer (matches the ``valid_erasure_*`` CHECK constraint\nin the migration)."},"AdminReferralGrantResponse":{"properties":{"id":{"title":"Id"},"referrer_user_id":{"title":"Referrer User Id"},"referrer_workspace_id":{"title":"Referrer Workspace Id"},"referred_user_id":{"title":"Referred User Id"},"referred_workspace_id":{"title":"Referred Workspace Id"},"referrer_bonus_memories":{"title":"Referrer Bonus Memories"},"referred_bonus_memories":{"title":"Referred Bonus Memories"},"granted_at":{"title":"Granted At"},"revoked_at":{"title":"Revoked At"},"revoked_reason":{"title":"Revoked Reason"}},"type":"object","required":["id","referrer_user_id","referrer_workspace_id","referred_user_id","referred_workspace_id","referrer_bonus_memories","referred_bonus_memories","granted_at","revoked_at","revoked_reason"],"title":"AdminReferralGrantResponse","description":"A ledger row with both sides visible."},"AdminReferralListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminReferralGrantResponse"},"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":"AdminReferralListResponse","description":"Paged ledger listing."},"AdminUpdatePlanRequest":{"properties":{"plan_name":{"type":"string","title":"Plan Name","description":"Target tier; one of the registered plan keys"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["plan_name"],"title":"AdminUpdatePlanRequest","description":"Request to update workspace plan tier."},"AdminWorkspacePlanInfo":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"plan_name":{"type":"string","title":"Plan Name"},"owner_user_id":{"type":"string","title":"Owner User Id"},"owner_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Name"},"owner_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Email"},"total_memories":{"type":"integer","title":"Total Memories"},"memory_limit":{"type":"integer","title":"Memory Limit"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day"},"mcp_calls_per_week":{"type":"integer","title":"Mcp Calls Per Week"}},"type":"object","required":["id","name","plan_name","owner_user_id","total_memories","memory_limit","mcp_calls_per_day","mcp_calls_per_week"],"title":"AdminWorkspacePlanInfo","description":"Workspace with plan information.\n\nIssue #276: Slug removed."},"AgentCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Workspace-unique name"},"description":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Description"},"framework":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Framework","description":"Free-form: 'claude-code', 'langgraph', ..."},"environment":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Environment","description":"Aligned with OTel deployment.environment.name"},"version":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Version","description":"Agent build/prompt version (client-reported)"}},"type":"object","required":["name"],"title":"AgentCreate","description":"Request model for registering an agent."},"AgentListResponse":{"properties":{"agents":{"items":{"$ref":"#/components/schemas/AgentResponse"},"type":"array","title":"Agents"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["agents","count"],"title":"AgentListResponse","description":"Response model for the workspace agent listing."},"AgentResponse":{"properties":{"id":{"title":"Id"},"workspace_id":{"title":"Workspace Id"},"name":{"title":"Name"},"description":{"title":"Description"},"owner_user_id":{"title":"Owner User Id"},"framework":{"title":"Framework"},"environment":{"title":"Environment"},"version":{"title":"Version"},"status":{"title":"Status"},"enforcement_mode":{"title":"Enforcement Mode"},"last_seen_at":{"title":"Last Seen At"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["id","workspace_id","name","owner_user_id","status","enforcement_mode","created_at","updated_at"],"title":"AgentResponse","description":"Response model for one registry row."},"AgentStateKeyResponse":{"properties":{"key":{"type":"string","title":"Key"}},"type":"object","required":["key"],"title":"AgentStateKeyResponse","description":"Envelope for a write that returns just the affected key (set / delete)."},"AgentStateListResponse":{"properties":{"states":{"additionalProperties":true,"type":"object","title":"States"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["states","count"],"title":"AgentStateListResponse","description":"Envelope for listing all live entries in a context."},"AgentStateSetRequest":{"properties":{"value":{"title":"Value","description":"Arbitrary JSON value to store (must not be null)"},"ttl_seconds":{"anyOf":[{"type":"integer","exclusiveMinimum":0.0},{"type":"null"}],"title":"Ttl Seconds","description":"Optional TTL in seconds (clamped server-side to 30 days). Omit to persist until explicitly deleted."}},"type":"object","required":["value"],"title":"AgentStateSetRequest","description":"Body for upserting an agent-state entry.\n\n``value`` is arbitrary JSON (stored in the JSONB column) but must not be\nnull — the column is NOT NULL, and a null value is rejected up front as a\n422 rather than surfacing as a DB-layer 500."},"AgentStateValueResponse":{"properties":{"key":{"type":"string","title":"Key"},"value":{"title":"Value"}},"type":"object","required":["key","value"],"title":"AgentStateValueResponse","description":"Envelope for a single-key read."},"AgentUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":10000},{"type":"null"}],"title":"Description"},"framework":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Framework"},"environment":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Environment"},"version":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Version"},"status":{"anyOf":[{"type":"string","enum":["active","suspended","retired"]},{"type":"null"}],"title":"Status","description":"Lifecycle kill switch (fail-closed for bound keys, P0-2)"},"enforcement_mode":{"anyOf":[{"type":"string","enum":["shadow","enforce"]},{"type":"null"}],"title":"Enforcement Mode","description":"Binding enforcement ramp; enforce→shadow is audited as widening"}},"type":"object","title":"AgentUpdate","description":"Request model for a partial agent update.\n\nOnly fields explicitly present in the request body are applied\n(``exclude_unset``), so ``null`` clears a metadata field while an absent\nkey leaves it untouched."},"AllowlistAddRequest":{"properties":{"provider":{"type":"string","enum":["github","google"],"title":"Provider","default":"github"},"github_username":{"anyOf":[{"type":"string","maxLength":39,"minLength":1,"pattern":"^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,37}[a-zA-Z0-9])?$"},{"type":"null"}],"title":"Github Username"},"email":{"anyOf":[{"type":"string","maxLength":247,"pattern":"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"},{"type":"null"}],"title":"Email"}},"type":"object","title":"AllowlistAddRequest","description":"Allowlist add payload (provider-aware since #655).\n\nThree accepted shapes:\n\n* GitHub legacy (pre-#655): ``{github_username}`` — preserved verbatim\n  so existing admin tooling keeps working without a flag day.\n* GitHub canonical: ``{provider: \"github\", github_username}`` — same\n  semantics, but with the discriminator explicit.\n* Google: ``{provider: \"google\", email}`` — resolves the user's\n  OIDC ``sub`` by looking up an existing ``users`` row with\n  ``auth_provider='google'`` and matching email (v1 bootstrap UX —\n  pre-OAuth invitation lands in Phase 2).\n\nPydantic validates ``github_username`` shape when present (1–39 chars,\nalphanumeric + hyphens, no leading/trailing hyphen) so malformed\npayloads don't spend a GitHub API request (rate-limited to 60/hr\nunauthenticated). The ``email`` field uses the same regex pattern\nthat the rest of the codebase uses for email-string validation\n(see ``models/schemas.py:830``); switching to ``pydantic.EmailStr``\nwould require pulling in ``email-validator`` as a new dependency,\nwhich is not warranted for a single field."},"AllowlistEntryResponse":{"properties":{"id":{"title":"Id"},"provider":{"title":"Provider"},"subject_id":{"title":"Subject Id"},"subject_label":{"title":"Subject Label"},"github_user_id":{"title":"Github User Id"},"github_username":{"title":"Github Username"},"source":{"title":"Source"},"state":{"title":"State"},"added_by_user_id":{"title":"Added By User Id"},"created_at":{"title":"Created At"}},"type":"object","required":["id","provider","subject_id","subject_label","github_user_id","github_username","source","state","added_by_user_id","created_at"],"title":"AllowlistEntryResponse","description":"Allowlist entry read-model.\n\n#655 added ``provider`` / ``subject_id`` / ``subject_label`` as the\ncanonical fields. The legacy ``github_user_id`` / ``github_username``\nfields are retained on the response for backward compatibility with\npre-#655 admin tooling — for ``provider='google'`` rows these carry\nthe sentinel values written by ``add_to_allowlist_entry``\n(``\"google:<sub>\"`` / email), which existing GitHub-only consumers\ncan ignore via the new ``provider`` field."},"AnalysisCancelResponse":{"properties":{"run_id":{"title":"Run Id"},"status":{"title":"Status"},"cancellation_reason":{"title":"Cancellation Reason"},"finished_at":{"title":"Finished At"}},"type":"object","required":["run_id","status","cancellation_reason","finished_at"],"title":"AnalysisCancelResponse","description":"DELETE /{run_id} response — confirms the soft-cancel.\n\nSame ``model_validate(row)`` pattern as AnalysisStartResponse so\nSQLAlchemy column types are bridged via Pydantic ``from_attributes``."},"AnalysisListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AnalysisRow"},"type":"array","title":"Items"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["items","next_cursor"],"title":"AnalysisListResponse","description":"Paginated list of runs."},"AnalysisPreviewRequest":{"properties":{"from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"},"to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"},"types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Types"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"min_importance":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Min Importance"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"},"model_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Model Id"}},"type":"object","title":"AnalysisPreviewRequest","description":"Pre-flight cost-estimate request body.\n\nMirrors the POST body so the modal in #497 can submit the same\npayload twice (preview, then run on confirm) without re-shaping.\nEmpty body is valid — the preview falls back to the count of\nevery memory in the context."},"AnalysisPreviewResponse":{"properties":{"memory_count":{"type":"integer","title":"Memory Count"},"cluster_count_estimate":{"type":"integer","title":"Cluster Count Estimate"},"estimated_cost_cents":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Cost Cents"},"model_id":{"type":"string","title":"Model Id"},"breakdown":{"additionalProperties":{"type":"integer"},"type":"object","title":"Breakdown"}},"type":"object","required":["memory_count","cluster_count_estimate","estimated_cost_cents","model_id","breakdown"],"title":"AnalysisPreviewResponse","description":"Cost-estimate output (Stage [A] from preview.py).\n\n``estimated_cost_cents`` is ``null`` when no price is configured for the\nmodel (#1570) — the estimate is unavailable, not zero — or when the\ndeployment disables cost display (``ENABLE_COST_DISPLAY=false``, #1571)."},"AnalysisRow":{"properties":{"run_id":{"title":"Run Id"},"workspace_id":{"title":"Workspace Id"},"context_id":{"title":"Context Id"},"status":{"title":"Status"},"triggered_by":{"title":"Triggered By"},"started_at":{"title":"Started At"},"finished_at":{"title":"Finished At"},"input_count":{"title":"Input Count"},"cost_estimated_cents":{"title":"Cost Estimated Cents"},"cost_actual_cents":{"title":"Cost Actual Cents"},"error":{"title":"Error"},"cancellation_reason":{"title":"Cancellation Reason"}},"type":"object","required":["run_id","workspace_id","context_id","status","triggered_by","started_at","finished_at","input_count","cost_estimated_cents","cost_actual_cents","error","cancellation_reason"],"title":"AnalysisRow","description":"One row in list / single-fetch responses.\n\nFields mirror ``MemoryAnalysis`` columns the API contract surfaces\n(#496 spec). Internal columns like ``model_snapshot`` are omitted\non purpose — the snapshot is a debugging artifact, not part of\nthe public contract, and adds ~500B per row to the list payload.\n\n``validation_alias=\"id\"`` (input-only) maps the ORM column ``id``\n→ ``run_id`` field while keeping the wire-format key as ``run_id``\nfor clients (matches the issue spec naming)."},"AnalysisStartRequest":{"properties":{"from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"},"to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"},"types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Types"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags"},"min_importance":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Min Importance"},"query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query"},"model_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Model Id"}},"type":"object","title":"AnalysisStartRequest","description":"POST /analyses body — same shape as preview (intentional)."},"AnalysisStartResponse":{"properties":{"run_id":{"title":"Run Id"},"status":{"title":"Status"},"started_at":{"title":"Started At"}},"type":"object","required":["run_id","status","started_at"],"title":"AnalysisStartResponse","description":"202 Accepted body.\n\nConstructed via ``model_validate(analysis)`` so the ORM row's\n``id`` column maps to ``run_id`` (matches the issue spec naming)\nand SQLAlchemy ``Column[datetime]`` ↔ Python ``datetime`` is\nbridged by Pydantic's ``from_attributes`` rather than manual\ncast at every call site."},"AnalysisUsage":{"properties":{"used_today":{"type":"integer","title":"Used Today","description":"Analyses started today (all statuses count)","default":0},"limit_today":{"type":"integer","title":"Limit Today","description":"Plan + addon daily limit","default":0},"addon_bonus":{"type":"integer","title":"Addon Bonus","description":"Addon-supplied bonus (extra_analysis_runs)","default":0},"remaining_today":{"type":"integer","title":"Remaining Today","description":"max(0, limit_today - used_today)","default":0},"resets_at":{"type":"string","title":"Resets At","description":"ISO-8601 timestamp of the next reset, in the caller's timezone (midnight of the next day). Always populated — the builder falls back to UTC when User.timezone is unset."}},"type":"object","required":["resets_at"],"title":"AnalysisUsage","description":"Memory Analysis daily quota usage (Issue #496).\n\nMirrors the response detail shape of the 429 quota-exceeded body\nso the dashboard, the new-analysis modal (#497), and the gate\nrejection all read the same field names."},"AuditVerifyResponse":{"properties":{"valid":{"type":"boolean","title":"Valid"},"entries":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Entries"},"head":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Head"},"broken_at":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Broken At"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"erasure_pseudonymized":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Erasure Pseudonymized"}},"type":"object","required":["valid"],"title":"AuditVerifyResponse","description":"Result of recomputing the workspace's tamper-evident audit chain."},"AvailablePlanInfo":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"quotas":{"additionalProperties":true,"type":"object","title":"Quotas"},"features":{"items":{"type":"string"},"type":"array","title":"Features"}},"type":"object","required":["name","display_name","quotas","features"],"title":"AvailablePlanInfo","description":"Available plan tier information."},"AvailableWorkerApp":{"properties":{"platform":{"type":"string","title":"Platform"},"app_key":{"type":"string","title":"App Key"},"display_name":{"type":"string","title":"Display Name"}},"type":"object","required":["platform","app_key","display_name"],"title":"AvailableWorkerApp","description":"Non-secret app identity that a workspace admin may bind."},"BetaInviteCreateRequest":{"properties":{"label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Label"}},"type":"object","title":"BetaInviteCreateRequest","description":"Optional body of ``POST /beta-invites`` (#1595). No body at all is valid."},"BetaInviteCreatedResponse":{"properties":{"id":{"title":"Id"},"url":{"title":"Url"},"expires_at":{"title":"Expires At"},"label":{"title":"Label"}},"type":"object","required":["id","url","expires_at","label"],"title":"BetaInviteCreatedResponse","description":"A freshly minted invite — the only payload that ever carries the URL.\n\nReturned by both create and reissue."},"BetaInviteItem":{"properties":{"id":{"title":"Id"},"status":{"title":"Status"},"label":{"title":"Label"},"created_at":{"title":"Created At"},"expires_at":{"title":"Expires At"},"redeemed_at":{"title":"Redeemed At"},"revoked_at":{"title":"Revoked At"},"redeemed_email":{"title":"Redeemed Email"}},"type":"object","required":["id","status","label","created_at","expires_at","redeemed_at","revoked_at","redeemed_email"],"title":"BetaInviteItem","description":"One invite as its inviter sees it.\n\nLifecycle, the inviter's own ``label``, and — for a ``redeemed`` invite whose\nadmitted account still exists — that account's current e-mail. #1581 showed\nstatus only; #1595 reverses that on purpose: the inviter is the person who\nvouched for the invitee, so this is not a new disclosure. ``redeemed_email``\nis ``null`` for every other status, and turns ``null`` again once the account\nis erased. Never the token, its hash or the URL."},"BetaInvitePreviewResponse":{"properties":{"valid":{"title":"Valid"},"expires_at":{"title":"Expires At"}},"type":"object","required":["valid","expires_at"],"title":"BetaInvitePreviewResponse","description":"What the public landing page may know about a usable link."},"BetaInviteSummaryResponse":{"properties":{"quota":{"title":"Quota"},"used":{"title":"Used"},"active":{"title":"Active"},"redeemed":{"title":"Redeemed"},"remaining":{"title":"Remaining"},"invites":{"title":"Invites"}},"type":"object","required":["quota","used","active","redeemed","remaining","invites"],"title":"BetaInviteSummaryResponse","description":"The caller's quota standing. ``null`` quota / remaining = unlimited (admin).\n\n``used`` counts the invites occupying a slot; ``active`` and ``redeemed`` are\nits two parts (``active + redeemed == used``), so a client never re-derives\nthem from the list."},"BillingHandoffRequest":{"properties":{"workspace_id":{"type":"string","format":"uuid","title":"Workspace Id","description":"The workspace to start a billing session for. The caller MUST be its owner — the owner check binds to this value, not the session's current workspace."}},"type":"object","required":["workspace_id"],"title":"BillingHandoffRequest","description":"Request to mint a handoff token for one owned workspace."},"BillingHandoffResponse":{"properties":{"token":{"title":"Token","description":"Ed25519-signed JWT (alg=Ed25519, RFC 9864)."},"token_type":{"title":"Token Type","description":"Token kind discriminator."},"kid":{"title":"Kid","description":"Signing key id — selects the verifier's public key."},"jti":{"title":"Jti","description":"Unique token id (verifier enforces single-use)."},"expires_at":{"title":"Expires At","description":"Token expiry (UTC, short-lived)."},"url":{"title":"Url","description":"Ready-to-use redirect URL ({base}/enter?t={token}) when payment_public_base_url is configured; null otherwise (the decoupled raw-token contract, #1098). The caller redirects the owner's browser here."}},"type":"object","required":["token","kid","jti","expires_at"],"title":"BillingHandoffResponse","description":"The minted handoff token and the metadata the billing host needs."},"BillingPlanPush":{"properties":{"plan_name":{"type":"string","title":"Plan Name","description":"Entitlement tier: one of the registered plan keys (free | basic | pro | promax)"},"status":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Status","description":"Billing subscription status (Stripe-agnostic free-form). Billing-owned; accepted for audit/forward-compat, NOT persisted or enforced here."},"current_period_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Current Period End","description":"Subscription period end. Billing-owned; accepted for audit, not persisted here."},"addons":{"anyOf":[{"additionalProperties":{"type":"integer"},"type":"object"},{"type":"null"}],"title":"Addons","description":"Absolute addon bonus values keyed by addon dimension (memory, mcp_quota, rest_quota, public_quota, member, context, analysis, storage_mb, sleep_contexts, connector). FULL REPLACE: when provided this is the complete addon state — dimensions omitted from the map are reset to 0 (an empty object zeros all). Omit the field entirely (null) to leave existing addons unchanged (tier-only change)."}},"type":"object","required":["plan_name"],"title":"BillingPlanPush","description":"Entitlement push from the billing service (#954 boundary contract)."},"BillingPlanPushResult":{"properties":{"workspace_id":{"title":"Workspace Id"},"plan_name":{"title":"Plan Name"},"addons":{"title":"Addons"},"entitlement_source":{"title":"Entitlement Source"},"status":{"title":"Status"},"current_period_end":{"title":"Current Period End"},"applied":{"title":"Applied"}},"type":"object","required":["workspace_id","plan_name","addons","entitlement_source"],"title":"BillingPlanPushResult","description":"Echo of the applied entitlement (idempotent)."},"BindingCreate":{"properties":{"context_id":{"type":"string","format":"uuid","title":"Context Id"},"can_read":{"type":"boolean","title":"Can Read","default":true},"write_policy":{"type":"string","enum":["deny","direct"],"title":"Write Policy","default":"deny"},"is_default":{"type":"boolean","title":"Is Default","description":"Bootstrap default binding (max one per agent)","default":false},"allowed_memory_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Memory Types","description":"Per-memory type filter (#1299): null = all types, [] = deny-all, else the memory types this binding may read (enforce-mode only). Applies to the memory-read lanes; enumeration/aggregate surfaces still use the context-level gate until #1301"},"allowed_source_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Source Types","description":"Per-memory source filter (#1299): null = all sources, [] = deny-all, else values from the memories.source_type set. Applies to the memory-read lanes; enumeration/aggregate surfaces still use the context-level gate until #1301"}},"type":"object","required":["context_id"],"title":"BindingCreate","description":"Request model for binding an agent to a context (subtractive scoping)."},"BindingListResponse":{"properties":{"bindings":{"items":{"$ref":"#/components/schemas/BindingResponse"},"type":"array","title":"Bindings"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["bindings","count"],"title":"BindingListResponse","description":"Response model for an agent's binding listing."},"BindingResponse":{"properties":{"id":{"title":"Id"},"agent_id":{"title":"Agent Id"},"context_id":{"title":"Context Id"},"can_read":{"title":"Can Read"},"write_policy":{"title":"Write Policy"},"is_default":{"title":"Is Default"},"allowed_memory_types":{"title":"Allowed Memory Types"},"allowed_source_types":{"title":"Allowed Source Types"},"created_by":{"title":"Created By"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["id","agent_id","context_id","can_read","write_policy","is_default","created_by","created_at","updated_at"],"title":"BindingResponse","description":"Response model for one binding row."},"BindingUpdate":{"properties":{"can_read":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Can Read"},"write_policy":{"anyOf":[{"type":"string","enum":["deny","direct"]},{"type":"null"}],"title":"Write Policy"},"is_default":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Default"},"allowed_memory_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Memory Types","description":"Per-memory type filter (#1299): null = all types, [] = deny-all"},"allowed_source_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Source Types","description":"Per-memory source filter (#1299): null = all sources, [] = deny-all"}},"type":"object","title":"BindingUpdate","description":"Partial binding update (``context_id`` is immutable — recreate to re-target)."},"Bm25DriftDetail":{"properties":{"id":{"title":"Id"},"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"measured_at":{"title":"Measured At"},"psi":{"title":"Psi"},"psi_status":{"title":"Psi Status"},"m_memory_points":{"title":"M Memory Points"},"r_resource_points":{"title":"R Resource Points"},"num_terms":{"title":"Num Terms"},"context_deleted":{"title":"Context Deleted"},"top_divergent_terms":{"title":"Top Divergent Terms"}},"type":"object","required":["id","context_id","measured_at","psi","psi_status","m_memory_points","r_resource_points","num_terms"],"title":"Bm25DriftDetail","description":"Drift log row, detail-view shape (includes top_divergent_terms)."},"Bm25DriftDetailResponse":{"properties":{"row":{"$ref":"#/components/schemas/Bm25DriftDetail"}},"type":"object","required":["row"],"title":"Bm25DriftDetailResponse"},"Bm25DriftListResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/Bm25DriftSummary"},"type":"array","title":"Rows"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["rows","total","limit","offset"],"title":"Bm25DriftListResponse"},"Bm25DriftRevealRequest":{"properties":{"reason":{"type":"string","minLength":10,"title":"Reason","description":"Justification for viewing plaintext terms (audit-logged)"}},"type":"object","required":["reason"],"title":"Bm25DriftRevealRequest","description":"Body for POST /admin/bm25-drift/{row_id}/reveal-terms."},"Bm25DriftRevealResponse":{"properties":{"id":{"title":"Id"},"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"measured_at":{"title":"Measured At"},"psi":{"title":"Psi"},"psi_status":{"title":"Psi Status"},"m_memory_points":{"title":"M Memory Points"},"r_resource_points":{"title":"R Resource Points"},"num_terms":{"title":"Num Terms"},"context_deleted":{"title":"Context Deleted"},"top_divergent_terms":{"title":"Top Divergent Terms"},"resolved_terms":{"title":"Resolved Terms"}},"type":"object","required":["id","context_id","measured_at","psi","psi_status","m_memory_points","r_resource_points","num_terms","resolved_terms"],"title":"Bm25DriftRevealResponse","description":"Drift detail + resolved plaintext terms."},"Bm25DriftRunRequest":{"properties":{"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}},"type":"object","title":"Bm25DriftRunRequest","description":"Body for POST /admin/bm25-drift/run.\n\nAttributes:\n    context_id: Target context. When null, runs against every active\n        context (matches the cron behaviour). For dev/staging\n        verification — production cron remains disabled until v0.14.0."},"Bm25DriftRunResponse":{"properties":{"scheduled_context_count":{"type":"integer","title":"Scheduled Context Count"}},"type":"object","required":["scheduled_context_count"],"title":"Bm25DriftRunResponse"},"Bm25DriftSummary":{"properties":{"id":{"title":"Id"},"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"measured_at":{"title":"Measured At"},"psi":{"title":"Psi"},"psi_status":{"title":"Psi Status"},"m_memory_points":{"title":"M Memory Points"},"r_resource_points":{"title":"R Resource Points"},"num_terms":{"title":"Num Terms"}},"type":"object","required":["id","context_id","measured_at","psi","psi_status","m_memory_points","r_resource_points","num_terms"],"title":"Bm25DriftSummary","description":"Drift log row, list-view shape (no top_divergent_terms)."},"Body_introspect_token_api_v1_oauth_introspect_post":{"properties":{"token":{"type":"string","title":"Token","description":"Access token to introspect"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Client identifier (client_secret_post; or use HTTP Basic)"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret","description":"Client secret (client_secret_post; or use HTTP Basic)"}},"type":"object","required":["token"],"title":"Body_introspect_token_api_v1_oauth_introspect_post"},"Body_oauth_revoke_api_v1_oauth_revoke_post":{"properties":{"token":{"type":"string","title":"Token"},"token_type_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Type Hint"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id","description":"Client identifier (public clients, client_secret_post)"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret","description":"Client secret (client_secret_post; or use HTTP Basic)"}},"type":"object","required":["token"],"title":"Body_oauth_revoke_api_v1_oauth_revoke_post"},"BootstrapRequest":{"properties":{"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"},"session_id":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Session Id"},"query":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Query"},"recall_k":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Recall K"},"pinned_cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pinned Cap"},"upcoming_until":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upcoming Until"},"include":{"anyOf":[{"items":{"type":"string","enum":["pinned","recall","upcoming","state","policy"]},"type":"array"},{"type":"null"}],"title":"Include"},"recall_evaluation":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Recall Evaluation"},"max_chars":{"anyOf":[{"type":"integer","maximum":100000.0,"minimum":10000.0},{"type":"null"}],"title":"Max Chars"}},"type":"object","title":"BootstrapRequest","description":"Body for POST /api/v1/agents/{agent_id}/bootstrap (agent_id from path).\n\nPOST-for-read follows the POST /api/v1/memory/pinned precedent."},"CapacityLockInfo":{"properties":{"memory_count":{"type":"integer","title":"Memory Count","description":"Live memories in the workspace"},"memory_limit":{"type":"integer","title":"Memory Limit","description":"Effective memory limit"},"over_memories":{"type":"integer","minimum":0.0,"title":"Over Memories","description":"Memories to remove to unlock"},"used_bytes":{"type":"integer","title":"Used Bytes","description":"File bytes the owner can still delete: uploaded, not soft-deleted files that are workspace-scoped or in a live context (#1961) — the figure downgrade-eligibility shares"},"storage_limit_bytes":{"type":"integer","title":"Storage Limit Bytes","description":"Effective storage limit in bytes"},"over_bytes":{"type":"integer","minimum":0.0,"title":"Over Bytes","description":"File bytes to remove to unlock"},"cleanup_url":{"type":"string","title":"Cleanup Url","description":"Web page to clean up or re-subscribe"},"purge_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Purge At","description":"When the workspace's memories, contexts and files are deleted if it is still over capacity (#1969): the 04:00 UTC daily sweep 30 days after that sweep first saw it over, ISO-8601 UTC. Null until that sweep has run, and while the purge is disabled on this deployment (CAPACITY_PURGE_ENABLED)."}},"type":"object","required":["memory_count","memory_limit","over_memories","used_bytes","storage_limit_bytes","over_bytes","cleanup_url"],"title":"CapacityLockInfo","description":"A Free workspace over its plan capacity (#1941).\n\nWhile present, search and saving are refused with ``CAPACITY-001``;\nlisting, deleting and export keep working. ``over_*`` is 0 on an axis that\nis within its limit."},"CapacityLockNoticeRequest":{"properties":{"period_end":{"type":"string","format":"date-time","title":"Period End","description":"When the paid period ends and the workspace returns to Free"}},"type":"object","required":["period_end"],"title":"CapacityLockNoticeRequest","description":"Body of ``POST /internal/workspaces/{id}/capacity-lock-notice`` (#1941)."},"CapacityLockNoticeResult":{"properties":{"sent":{"type":"boolean","title":"Sent"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"over_memories":{"type":"integer","title":"Over Memories"},"over_bytes":{"type":"integer","title":"Over Bytes"}},"type":"object","required":["sent","over_memories","over_bytes"],"title":"CapacityLockNoticeResult","description":"Outcome of a notice request. Never carries the recipient address.\n\n``reason`` (when ``sent`` is false): ``not_billing_managed`` (the\nworkspace's entitlement is admin-managed or self-hosted — it is never\nlocked, so it is never warned), ``within_capacity`` (the projected\nFree state fits — nothing to warn about), ``already_sent`` (a notice for\nthis ``period_end`` was already sent), ``cooldown`` (a notice for this\nworkspace was sent in the last 24 hours, for any ``period_end``),\n``no_owner_email`` or ``delivery_failed`` (retry later; both claims are\nreleased).\n``sent=true, reason=\"delivery_uncertain\"``: the provider timed out after it\nmay have accepted the email; the claim is kept, so it is never resent."},"ClusterListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ClusterRow"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"ClusterListResponse","description":"All clusters for a run, ordered by ``cluster_index``."},"ClusterRow":{"properties":{"cluster_index":{"type":"integer","title":"Cluster Index"},"label":{"type":"string","title":"Label"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"count":{"type":"integer","title":"Count"},"centroid_2d":{"items":{"type":"number"},"type":"array","title":"Centroid 2D"},"representative_memory_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Representative Memory Ids"},"property_stats":{"additionalProperties":true,"type":"object","title":"Property Stats"},"label_confidence":{"type":"number","title":"Label Confidence"}},"type":"object","required":["cluster_index","label","description","count","centroid_2d","representative_memory_ids","property_stats","label_confidence"],"title":"ClusterRow","description":"One cluster within an analysis run.\n\nMirrors ``MemoryAnalysisCluster`` columns the API contract surfaces\n(#497 frontend). Internal columns (``analysis_id``, ``parent_id``,\ncluster ``id`` UUID PK) are omitted: the wire identifier is the\nuser-visible ``cluster_index`` ordinal, not the DB UUID.\n\n``representative_memory_ids`` is the raw list as stored — the\nfrontend resolves these to memory summaries via a follow-up\n``recall(filters={\"id\": [...]})`` call rather than embedding the\nsummaries in this response (keeps the cluster list payload small\nand lets the recall layer apply its own importance / freshness\nsorting)."},"ConfigBatchRequest":{"properties":{"updates":{"additionalProperties":true,"type":"object","title":"Updates"}},"type":"object","required":["updates"],"title":"ConfigBatchRequest","description":"Batch update configuration."},"ConfigListResponse":{"properties":{"configs":{"items":{"$ref":"#/components/schemas/ConfigValue"},"type":"array","title":"Configs"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["configs","total"],"title":"ConfigListResponse","description":"Configuration list response."},"ConfigUpdateRequest":{"properties":{"value":{"title":"Value"}},"type":"object","required":["value"],"title":"ConfigUpdateRequest","description":"Update configuration value."},"ConfigValidateRequest":{"properties":{"key":{"type":"string","title":"Key"},"value":{"title":"Value"}},"type":"object","required":["key","value"],"title":"ConfigValidateRequest","description":"Validate configuration."},"ConfigValue":{"properties":{"key":{"type":"string","title":"Key"},"value":{"title":"Value"},"category":{"type":"string","title":"Category"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"is_sensitive":{"type":"boolean","title":"Is Sensitive","default":false},"read_only":{"type":"boolean","title":"Read Only","default":true}},"type":"object","required":["key","value","category"],"title":"ConfigValue","description":"Single configuration value."},"ConnectorChannel":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"is_private":{"type":"boolean","title":"Is Private"},"is_member":{"type":"boolean","title":"Is Member"}},"type":"object","required":["id","name","is_private","is_member"],"title":"ConnectorChannel","description":"One selectable channel for the settings picker (#1391).\n\nMinimized by design: identity (id, name) plus the two flags the picker has\nto render — ``is_private`` and, since #1451, ``is_member``. Everything else\nSlack returns (member counts, topics, purpose, creator, timestamps) is\ndropped and never reaches the browser."},"ConnectorChannelsResponse":{"properties":{"channels":{"items":{"$ref":"#/components/schemas/ConnectorChannel"},"type":"array","title":"Channels"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"missing_scopes":{"items":{"type":"string"},"type":"array","title":"Missing Scopes"}},"type":"object","required":["channels"],"title":"ConnectorChannelsResponse","description":"A page of channels plus Slack's opaque forward cursor (#1391)."},"ContextCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"pattern":"^[a-z0-9_-]+$","title":"Name","description":"Context name (lowercase alphanumeric, hyphen, underscore)"},"display_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Display Name","description":"Human-readable display name (optional, defaults to name)"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description","description":"Optional context description"},"summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Summary","description":"LLM-oriented context summary"},"usage_guide":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Usage Guide","description":"LLM-oriented memory usage guidelines"},"embedding_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Embedding Model","description":"Embedding model for this context. Default: global EMBEDDING_MODEL setting. Immutable after creation."},"is_private":{"type":"boolean","title":"Is Private","description":"Privacy: TRUE=private (creator only), FALSE=shared (workspace members)","default":true}},"type":"object","required":["name"],"title":"ContextCreate","description":"Request model for creating a context.\n\nIssue #146: Now requires embedding model selection (immutable after creation)."},"ContextExportResponse":{"properties":{"format_version":{"title":"Format Version"},"exported_at":{"title":"Exported At"},"context":{},"search_config":{},"memory_count":{"title":"Memory Count"},"memories":{"title":"Memories"}},"type":"object","required":["exported_at","context","search_config","memory_count","memories"],"title":"ContextExportResponse","description":"GDPR Art.20-supporting context portability export (Issue #950).\n\nA point-in-time JSON snapshot of a context the caller can read: its\nmetadata, search configuration, and every memory visible to the caller\n(private context -> creator-only; shared -> all members) — the same\nvisibility model as ``GET /memory/list``. Vectors, neural edges, and\nsessions are intentionally omitted: they are regenerated / re-learned on\nre-import. This is a context-scoped *portability* export (anti-lock-in),\nnot a per-user Art.20 subject-access request — a created_by-filtered,\ncross-context personal-data export is a separate follow-up."},"ContextFilesDeleteResponse":{"properties":{"deleted":{"type":"integer","minimum":0.0,"title":"Deleted","description":"Uploaded files soft-deleted — what the file list showed; cancelled in-flight uploads release their bytes but are not counted"},"released_bytes":{"type":"integer","minimum":0.0,"title":"Released Bytes","description":"Bytes released from the storage quota: the uploaded files' bytes plus the reservations of cancelled in-flight uploads"}},"type":"object","required":["deleted","released_bytes"],"title":"ContextFilesDeleteResponse","description":"What ``DELETE /contexts/{id}/files`` removed (#1960)."},"ContextListResponse":{"properties":{"contexts":{"items":{"$ref":"#/components/schemas/ContextResponse"},"type":"array","title":"Contexts","description":"List of contexts"},"total":{"type":"integer","title":"Total","description":"Total number of contexts"}},"type":"object","required":["contexts","total"],"title":"ContextListResponse","description":"Response model for context list."},"ContextMemberResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"user_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Name"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email"},"role":{"type":"string","title":"Role"},"added_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Added At"},"is_workspace_admin":{"type":"boolean","title":"Is Workspace Admin","default":false}},"type":"object","required":["user_id","role"],"title":"ContextMemberResponse","description":"Response model for context member."},"ContextRecoveryRequest":{"properties":{"context_id":{"type":"string","title":"Context Id"},"workspace_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Id"},"context_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Name"},"dry_run":{"type":"boolean","title":"Dry Run","default":true}},"type":"object","required":["context_id"],"title":"ContextRecoveryRequest","description":"Request to recover a deleted context from Qdrant data."},"ContextRecoveryResponse":{"properties":{"context_id":{"type":"string","title":"Context Id"},"workspace_id":{"type":"string","title":"Workspace Id"},"qdrant_points_found":{"type":"integer","title":"Qdrant Points Found"},"memories_recovered":{"type":"integer","title":"Memories Recovered"},"memories_already_existed":{"type":"integer","title":"Memories Already Existed"},"context_record_created":{"type":"boolean","title":"Context Record Created"},"search_config_restored":{"type":"boolean","title":"Search Config Restored"},"dry_run":{"type":"boolean","title":"Dry Run"},"errors":{"items":{"type":"string"},"type":"array","title":"Errors"}},"type":"object","required":["context_id","workspace_id","qdrant_points_found","memories_recovered","memories_already_existed","context_record_created","search_config_restored","dry_run","errors"],"title":"ContextRecoveryResponse","description":"Result of context recovery attempt."},"ContextResponse":{"properties":{"id":{"title":"Id","description":"Context UUID"},"name":{"title":"Name","description":"Context name"},"display_name":{"title":"Display Name","description":"Human-readable display name"},"description":{"title":"Description","description":"Context description"},"summary":{"title":"Summary","description":"LLM-oriented context summary"},"usage_guide":{"title":"Usage Guide","description":"LLM-oriented usage guidelines"},"is_default":{"title":"Is Default","description":"Whether this is the default context"},"is_private":{"title":"Is Private","description":"Privacy: TRUE=private, FALSE=shared"},"is_public":{"title":"Is Public","description":"Public: TRUE=external API access, FALSE=internal"},"resource_id":{"title":"Resource Id","description":"Resource ID for public contexts"},"writes_paused":{"title":"Writes Paused","description":"Content writes paused: the plan has no slot for this context"},"is_locked":{"title":"Is Locked","description":"When true, deletion is prevented until unlocked"},"sleep_mode":{"title":"Sleep Mode","description":"Sleep maintenance mode: full, edges_only, or skip"},"created_by":{"title":"Created By","description":"Creator user ID"},"created_by_name":{"title":"Created By Name","description":"Creator name"},"created_at":{"title":"Created At","description":"Creation timestamp"},"updated_at":{"title":"Updated At","description":"Last update timestamp"},"use_rerank":{"title":"Use Rerank","description":"Reranking enabled (Basic+ only)"},"reranker_provider":{"title":"Reranker Provider","description":"Reranker provider: voyage/cohere"},"embedding_model":{"title":"Embedding Model","description":"Embedding model used for this context"},"embedding_dimensions":{"title":"Embedding Dimensions","description":"Embedding vector dimensions"},"member_count":{"title":"Member Count","description":"Number of members with access to this context"},"memory_count":{"title":"Memory Count","description":"Number of active memories in this context"},"last_activity_at":{"title":"Last Activity At","description":"Most recent memory activity (max of updated_at across memories)"}},"type":"object","required":["id","name","is_default","created_at"],"title":"ContextResponse","description":"Response model for context data."},"ContextRestoreRequest":{"properties":{"dry_run":{"type":"boolean","title":"Dry Run","description":"Count only (default); false restores.","default":true},"new_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Name","description":"Restore under this name when a live context has taken the old one."}},"type":"object","title":"ContextRestoreRequest","description":"Restore a soft-deleted context from its rows."},"ContextRestoreResponse":{"properties":{"context_id":{"type":"string","title":"Context Id"},"workspace_id":{"type":"string","title":"Workspace Id"},"name":{"type":"string","title":"Name"},"renamed_from":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Renamed From"},"deleted_at":{"type":"string","title":"Deleted At"},"deleted_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Deleted By"},"memories_restored":{"type":"integer","title":"Memories Restored"},"memories_left_deleted":{"type":"integer","title":"Memories Left Deleted"},"files_restored":{"type":"integer","title":"Files Restored","description":"Files the deletion purged that come back with the context (#1960)","default":0},"file_bytes_restored":{"type":"integer","title":"File Bytes Restored","description":"Their bytes, re-added to the storage usage","default":0},"warnings":{"items":{"type":"string"},"type":"array","title":"Warnings"},"dry_run":{"type":"boolean","title":"Dry Run"}},"type":"object","required":["context_id","workspace_id","name","renamed_from","deleted_at","deleted_by","memories_restored","memories_left_deleted","warnings","dry_run"],"title":"ContextRestoreResponse","description":"What a context restore did, or would do on a dry run."},"ContextSearchConfigResponse":{"properties":{"context_id":{"title":"Context Id"},"semantic_weight":{"title":"Semantic Weight","description":"Semantic search weight (0.0-1.0)"},"bm25_weight":{"title":"Bm25 Weight","description":"BM25 keyword search weight (0.0-1.0)"},"fetch_factor":{"title":"Fetch Factor","description":"Candidate retrieval multiplier (1-10)"},"use_rerank":{"title":"Use Rerank","description":"Enable/disable reranking"},"reranker_provider":{"title":"Reranker Provider","description":"Reranker provider (voyage/cohere)"},"reranker_model":{"title":"Reranker Model","description":"Provider-specific model name"},"embedding_model":{"title":"Embedding Model","description":"Embedding model (immutable)"},"embedding_dimensions":{"title":"Embedding Dimensions","description":"Vector dimensions (immutable)"},"reinforce_enabled":{"title":"Reinforce Enabled","description":"Bounded adoption+feedback recall re-rank enabled"},"reinforce_max_boost":{"title":"Reinforce Max Boost","description":"Bound on the reinforce adjustment (factor in [1-b, 1+b])"},"reinforce_require_host_arbitration":{"title":"Reinforce Require Host Arbitration","description":"Count only host-arbitrated (provenance='host') feedback"},"routing_mode":{"title":"Routing Mode","description":"Query-intent router: off | log_only (telemetry only) | active (opt-in)"},"content_chunks_enabled":{"title":"Content Chunks Enabled","description":"Embed long content as chunk vectors for dense retrieval"},"content_chunks_can_change":{"title":"Content Chunks Can Change","description":"Whether the caller may change content_chunks_enabled: the context owner or a workspace owner/admin, and, to turn it on, a vector store that keeps chunk vectors and (where gated by plan) the plan's feature and a free slot under its cap (#2103). Turning it off is always allowed"},"content_chunks_supported":{"title":"Content Chunks Supported","description":"Whether the vector store keeps content chunk vectors (false on LanceDB)"},"embedding_provider":{"title":"Embedding Provider","description":"Provider of the context's embedding model (openai / self_hosted): where memory content goes when content_chunks_enabled is on"},"content_chunks_backfill_pending":{"title":"Content Chunks Backfill Pending","description":"Long memories (1,500+ characters) still waiting for their chunk vectors, e.g. the existing memories queued when the flag was turned on; 0 while it is off"},"content_chunks_effective":{"title":"Content Chunks Effective","description":"Whether the context's content chunk vectors are read and written: content_chunks_enabled on, a vector store that keeps them, and, where the deployment gates them by plan (CONTENT_CHUNKS_REQUIRE_PLAN), a plan with the content_chunks feature on which this context is among the earliest-enabled max_content_chunk_contexts. An inactive context keeps the chunk vectors of unchanged memories; memories written or edited meanwhile get chunks only on their next edit or re-embed (nothing is re-queued when it becomes active again)"},"content_chunks_limit":{"title":"Content Chunks Limit","description":"The workspace plan's max_content_chunk_contexts (0 without the content_chunks feature); null when the deployment does not gate content chunks by plan"},"content_chunks_in_use":{"title":"Content Chunks In Use","description":"Live contexts of the workspace with content_chunks_enabled on, this one included when it is on"},"content_chunks_disabled_reason":{"title":"Content Chunks Disabled Reason","description":"Why the toggle cannot be turned on (flag off) or why the chunks are inactive (flag on): unsupported (LanceDB), plan (the plan lacks content_chunks), cap (max_content_chunk_contexts reached), or permission (nothing else blocks, but only the context owner or a workspace owner/admin may change it); null when nothing blocks"},"llm_selector_enabled":{"title":"Llm Selector Enabled","description":"Recall LLM selector opt-in: an MCP recall may send this context's summaries and attached content to the deployment's judge model, which keeps the results that answer the query and cuts their content to verbatim passages"},"llm_selector_effective":{"title":"Llm Selector Effective","description":"Whether recall acts on llm_selector_enabled: on, and, where the deployment gates it by plan (LLM_SELECTOR_REQUIRE_PLAN), a plan with the llm_selector feature on which this context is among the earliest-enabled max_llm_selector_contexts"},"llm_selector_limit":{"title":"Llm Selector Limit","description":"The workspace plan's max_llm_selector_contexts (0 without the llm_selector feature); null when the deployment does not gate the selector by plan"},"llm_selector_in_use":{"title":"Llm Selector In Use","description":"Live contexts of the workspace with llm_selector_enabled on, this one included when it is on"},"llm_selector_disabled_reason":{"title":"Llm Selector Disabled Reason","description":"Why the toggle cannot be turned on (flag off) or why the selector is inactive (flag on): plan (the plan lacks llm_selector), cap (max_llm_selector_contexts reached), or permission (nothing else blocks, but only the context owner or a workspace owner/admin may change it); null when nothing blocks"},"llm_selector_can_change":{"title":"Llm Selector Can Change","description":"Whether the caller may change llm_selector_enabled: the context owner or a workspace owner/admin, and, to turn it on where gated by plan, the plan's feature and a free slot under its cap. Turning it off is always allowed"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["context_id","semantic_weight","bm25_weight","fetch_factor","use_rerank","reranker_provider","reranker_model","embedding_model","embedding_dimensions","created_at","updated_at"],"title":"ContextSearchConfigResponse","description":"Response schema for context search configuration.\n\nIssue #130: Context-scoped Search & Reranker Settings\nIssue #146: Added immutable embedding configuration fields\nIssue #160: Renamed from Project to Context"},"ContextSearchConfigUpdate":{"properties":{"semantic_weight":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Semantic Weight","description":"Semantic search weight"},"bm25_weight":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Bm25 Weight","description":"BM25 keyword search weight"},"fetch_factor":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Fetch Factor","description":"Candidate retrieval multiplier"},"use_rerank":{"type":"boolean","title":"Use Rerank","description":"Enable/disable reranking"},"reranker_provider":{"type":"string","title":"Reranker Provider","description":"Reranker provider: 'voyage', 'cohere', or 'self_hosted'"},"reranker_model":{"type":"string","title":"Reranker Model","description":"Provider-specific model name"},"reinforce_enabled":{"type":"boolean","title":"Reinforce Enabled","description":"Enable the bounded adoption+feedback recall re-rank","default":true},"reinforce_max_boost":{"type":"number","maximum":0.5,"minimum":0.0,"title":"Reinforce Max Boost","description":"Bound on the reinforce adjustment (factor stays in [1-b, 1+b])","default":0.15},"reinforce_require_host_arbitration":{"type":"boolean","title":"Reinforce Require Host Arbitration","description":"Forge-resistant mode — only host-arbitrated feedback (provenance='host') moves ranking; an untrusted agent's self-feedback is ignored","default":false},"routing_mode":{"type":"string","pattern":"^(off|log_only|active)$","title":"Routing Mode","description":"Query-intent router: off | log_only (stamp telemetry, zero ranking change) | active (routed lane used only when search_mode is omitted)","default":"off"},"content_chunks_enabled":{"type":"boolean","title":"Content Chunks Enabled","description":"Embed long content (>= 1500 chars) as chunk vectors so dense retrieval sees the body, not only the summary. Turning it on also queues the context's existing long memories for chunking (drained in the background; see content_chunks_backfill_pending); turning it off deletes the chunks. When on, memory content (not only summaries) is sent to the embedding and rerank providers. Changing it takes the context owner or a workspace owner/admin (403 for an editor); sending the stored value is a no-op. Needs the Qdrant vector store: turning it on with LanceDB is refused (422)","default":false},"llm_selector_enabled":{"type":"boolean","title":"Llm Selector Enabled","description":"Recall LLM selector opt-in: an MCP recall may send this context's summaries and attached content to the deployment's judge model, which keeps the results that answer the query and cuts their content to verbatim passages. Changing it takes the context owner or a workspace owner/admin (403 for an editor); sending the stored value is a no-op. Where the deployment gates it by plan (LLM_SELECTOR_REQUIRE_PLAN), turning it on needs the llm_selector feature (403 FEAT-001) and a free slot under max_llm_selector_contexts (429 QUOTA-001)","default":false}},"type":"object","required":["semantic_weight","bm25_weight","fetch_factor","use_rerank","reranker_provider","reranker_model"],"title":"ContextSearchConfigUpdate","description":"Request schema for updating context search configuration.\n\nIssue #160: Renamed from ProjectSearchConfigUpdate to ContextSearchConfigUpdate"},"ContextStats":{"properties":{"context_id":{"type":"string","title":"Context Id"},"context_name":{"type":"string","title":"Context Name"},"created_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By"},"created_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Name"},"memory_count":{"type":"integer","title":"Memory Count"},"is_private":{"type":"boolean","title":"Is Private","default":false},"unsearchable_count":{"type":"integer","title":"Unsearchable Count","default":0},"stalled_count":{"type":"integer","title":"Stalled Count","default":0}},"type":"object","required":["context_id","context_name","created_by","created_by_name","memory_count"],"title":"ContextStats","description":"Statistics for a single context.\n\nSingle Collection Migration: storage_mb removed (memory count only)."},"ContextStatsItem":{"properties":{"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"memory_count":{"title":"Memory Count"},"last_activity":{"title":"Last Activity"},"member_count":{"title":"Member Count"},"api_calls_week":{"title":"Api Calls Week"},"active_users_week":{"title":"Active Users Week"},"avg_response_time_ms":{"title":"Avg Response Time Ms"}},"type":"object","required":["context_id","context_name","memory_count","last_activity","member_count"],"title":"ContextStatsItem","description":"Context usage statistics item.\n\nIssue #249: Per-context statistics for workspace overview."},"ContextStatsResponse":{"properties":{"context_id":{"type":"string","format":"uuid","title":"Context Id","description":"Context UUID"},"context_name":{"type":"string","title":"Context Name","description":"Context name"},"memory_count":{"type":"integer","title":"Memory Count","description":"Number of memories in context"},"status":{"type":"string","title":"Status","description":"Context status"}},"type":"object","required":["context_id","context_name","memory_count","status"],"title":"ContextStatsResponse","description":"Response model for context statistics.\n\nSingle Collection Migration: Stats now from PostgreSQL (memory_count)."},"ContextTagsResponse":{"properties":{"context_id":{"type":"string","format":"uuid","title":"Context Id","description":"Context UUID"},"context_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Name","description":"Context name (#1669; same value as MCP list_tags)."},"tags":{"items":{"$ref":"#/components/schemas/RelatedTagItem"},"type":"array","title":"Tags","description":"Aggregated tag info, sorted per the `sort` parameter."},"total":{"type":"integer","title":"Total","description":"Number of tags returned (post-filter, post-limit)."}},"type":"object","required":["context_id","tags","total"],"title":"ContextTagsResponse","description":"Response model for ``GET /contexts/{context_id}/tags`` (Issue #614).\n\nEnvelope wrapper consistent with ``ContextListResponse``. ``RelatedTagItem``\ninherits ``TZAwareBaseModel`` so the per-item ``last_used_at`` datetime\nserializes with an explicit UTC ``Z`` suffix when populated."},"ContextUpdate":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Display Name","description":"Updated display name"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description","description":"Updated context description"},"summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Summary","description":"Updated context summary"},"usage_guide":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Usage Guide","description":"Updated usage guidelines"},"is_private":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Private","description":"Privacy flag (Migration 034: Shared → Private removes non-owner members)"},"is_public":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Public","description":"Public flag (Issue #238: Public REST API access)"},"resource_id":{"anyOf":[{"type":"string","maxLength":255,"pattern":"^[a-z0-9_-]+$"},{"type":"null"}],"title":"Resource Id","description":"Resource ID for contexts (lowercase alphanumeric, underscore, and hyphen)"},"is_locked":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Locked","description":"When true, prevents this context from being deleted until unlocked."},"sleep_mode":{"anyOf":[{"type":"string","enum":["full","edges_only","skip"]},{"type":"null"}],"title":"Sleep Mode","description":"Sleep maintenance mode: full=all phases, edges_only=edge discovery+reindex, skip=none"}},"type":"object","title":"ContextUpdate","description":"Request model for updating a context."},"ContextUsageTimelineResponse":{"properties":{"context_id":{"type":"string","title":"Context Id"},"context_name":{"type":"string","title":"Context Name"},"daily_usage":{"items":{"$ref":"#/components/schemas/DailyUsageItem"},"type":"array","title":"Daily Usage"},"total_calls":{"type":"integer","title":"Total Calls"}},"type":"object","required":["context_id","context_name","daily_usage","total_calls"],"title":"ContextUsageTimelineResponse","description":"Response model for context usage timeline.\n\nIssue #249: GET /api/v1/workspaces/{workspace_id}/contexts/{context_id}/usage-timeline"},"ContextUserActivityResponse":{"properties":{"context_id":{"type":"string","title":"Context Id"},"context_name":{"type":"string","title":"Context Name"},"users":{"items":{"$ref":"#/components/schemas/UserActivityItem"},"type":"array","title":"Users"},"total_users":{"type":"integer","title":"Total Users"}},"type":"object","required":["context_id","context_name","users","total_users"],"title":"ContextUserActivityResponse","description":"Response model for context user activity.\n\nIssue #249: GET /api/v1/workspaces/{workspace_id}/contexts/{context_id}/user-activity"},"CostAggregationResponse":{"properties":{"rows":{"title":"Rows"}},"type":"object","required":["rows"],"title":"CostAggregationResponse","description":"Wrapper around the row list — keeps room for a future cursor /\nsummary block without breaking the response shape."},"CostAggregationRowResponse":{"properties":{"period_start":{"title":"Period Start"},"workspace_id":{"title":"Workspace Id"},"workspace_name":{"title":"Workspace Name"},"user_id":{"title":"User Id"},"user_email":{"title":"User Email"},"calls":{"title":"Calls"},"tokens_in":{"title":"Tokens In"},"tokens_out":{"title":"Tokens Out"},"tokens_cached_in":{"title":"Tokens Cached In"},"tokens_cache_write":{"title":"Tokens Cache Write"},"embedding_tokens":{"title":"Embedding Tokens"},"cost_usd":{"title":"Cost Usd"},"cost_usd_byok":{"title":"Cost Usd Byok"},"cost_breakdown_by_model":{"title":"Cost Breakdown By Model"},"cost_breakdown_by_source":{"title":"Cost Breakdown By Source"}},"type":"object","required":["period_start","workspace_id","user_id","calls","tokens_in","tokens_out","tokens_cached_in","tokens_cache_write","embedding_tokens","cost_usd","cost_usd_byok","cost_breakdown_by_model","cost_breakdown_by_source"],"title":"CostAggregationRowResponse","description":"One (period × workspace × user) row in the aggregation response.\n\n``cost_usd`` is the platform-billed total (used for B2B invoicing);\n``cost_usd_byok`` is the workspace's BYOK observability total\n(informational only — Kagura does not bill it). The split is\nenforced at the SQL level so it cannot accidentally re-merge.\n\nBoth cost fields are nullable: ``null`` means \"cost unknown\" (some\ncontributing usage row had no resolved pricing). The UI should\nrender NULL as \"—\" rather than \"$0.00\" to make the distinction\nvisible to operators."},"CostBreakdownByModelResponse":{"properties":{"model":{"title":"Model"},"calls":{"title":"Calls"},"cost_usd":{"title":"Cost Usd"},"cost_usd_byok":{"title":"Cost Usd Byok"}},"type":"object","required":["model","calls","cost_usd","cost_usd_byok"],"title":"CostBreakdownByModelResponse","description":"Per-model cost split inside a CostAggregationRowResponse.\n\n``cost_usd`` / ``cost_usd_byok`` are nullable: ``null`` means\n\"cost unknown\" (some contributing usage row had no resolved\npricing — e.g. a model with no row in ``llm_pricing`` at the\nrun's ``started_at``). Distinguishes \"no pricing snapshot\" from\n\"genuinely $0\", same semantics as ``LLMPricingService.lookup()``\nreturning ``None`` on a miss."},"CostBreakdownBySourceResponse":{"properties":{"source":{"title":"Source"},"calls":{"title":"Calls"},"cost_usd":{"title":"Cost Usd"},"cost_usd_byok":{"title":"Cost Usd Byok"}},"type":"object","required":["source","calls","cost_usd","cost_usd_byok"],"title":"CostBreakdownBySourceResponse","description":"Per-source cost split inside a CostAggregationRowResponse.\n\n``cost_usd`` / ``cost_usd_byok`` are nullable for the same\nreason documented on ``CostBreakdownByModelResponse``."},"CreateAPIKeyRequest":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name"},"auto_hide_minutes":{"type":"integer","title":"Auto Hide Minutes","default":10},"bound_context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bound Context Id"},"expires_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":1.0},{"type":"null"}],"title":"Expires Days"},"agent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Agent Id"}},"type":"object","required":["name"],"title":"CreateAPIKeyRequest","description":"Request for creating a new API key.\n\nIssue #626: ``bound_context_id`` makes this a public-bound key that is\nattributed to one ``is_public=true`` context (immutable binding —\nrevoke and re-create to change). Mutually exclusive with the\nworkspace-scoping derived from the URL: when supplied, the key is\nstored with ``workspace_id=NULL``."},"CreateEdgeRequest":{"properties":{"context_id":{"type":"string","format":"uuid","title":"Context Id"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"target_id":{"type":"string","format":"uuid","title":"Target Id"},"edge_type":{"type":"string","title":"Edge Type","default":"related_to"},"weight":{"type":"number","maximum":3.0,"minimum":0.0,"title":"Weight","default":1.0},"confidence":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Confidence","default":1.0},"overwrite":{"type":"boolean","title":"Overwrite","default":false}},"type":"object","required":["context_id","source_id","target_id"],"title":"CreateEdgeRequest","description":"Body for ``POST /graph/edges`` (#1416).\n\nMirrors the MCP ``create_edge`` tool's inputs (edge_type default,\nweight/confidence ranges, overwrite semantics) so the two transports stay\ncontract-consistent. ``context_id`` scopes the write; both endpoints must be\nlive memories in that context (enforced by the route)."},"CreateEdgeResponse":{"properties":{"operation":{"type":"string","title":"Operation"},"edge":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Edge"},"previous":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Previous"}},"type":"object","required":["operation","edge"],"title":"CreateEdgeResponse","description":"Response for ``POST /graph/edges``.\n\n``operation`` is one of created / updated / unchanged (conflict surfaces as a\n409, never a 200 body). ``edge`` is the serialized post-state; ``previous``\nis the pre-image when an existing non-protected edge was upserted."},"CredentialsStatusInfo":{"properties":{"api_key_count":{"type":"integer","title":"Api Key Count","default":0},"api_key_visible":{"type":"boolean","title":"Api Key Visible","default":false},"claude_app_visible":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Claude App Visible"},"chatgpt_app_visible":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Chatgpt App Visible"},"custom_app_count":{"type":"integer","title":"Custom App Count","default":0}},"type":"object","title":"CredentialsStatusInfo","description":"Credentials status information for member."},"CurrentUsage":{"properties":{"memory_count":{"type":"integer","title":"Memory Count","description":"Current memory count"},"api_calls_today":{"type":"integer","title":"Api Calls Today","description":"API calls today (all APIs)"},"api_calls_this_week":{"type":"integer","title":"Api Calls This Week","description":"API calls this week (all APIs)"},"mcp_calls_today":{"type":"integer","title":"Mcp Calls Today","description":"MCP calls today","default":0},"mcp_calls_this_week":{"type":"integer","title":"Mcp Calls This Week","description":"MCP calls this week","default":0},"rest_calls_today":{"type":"integer","title":"Rest Calls Today","description":"REST API calls today (non-public)","default":0},"rest_calls_this_week":{"type":"integer","title":"Rest Calls This Week","description":"REST API calls this week (non-public)","default":0},"public_calls_today":{"type":"integer","title":"Public Calls Today","description":"Public REST API calls today","default":0},"public_calls_this_week":{"type":"integer","title":"Public Calls This Week","description":"Public REST API calls this week","default":0},"memories_created_today":{"type":"integer","title":"Memories Created Today","description":"Memories created today, UTC (Issue #1549 daily quota counter)","default":0},"analysis":{"anyOf":[{"$ref":"#/components/schemas/AnalysisUsage"},{"type":"null"}],"description":"Memory Analysis daily quota stats (Issue #496). NULL when the caller has no current workspace selected."},"sleep_contexts":{"anyOf":[{"$ref":"#/components/schemas/SleepContextsUsage"},{"type":"null"}],"description":"Sleep-enabled contexts quota stats (Issue #560). NULL when the caller has no current workspace selected."},"workspaces":{"$ref":"#/components/schemas/WorkspacesUsage","description":"Owned-workspace cap usage for the caller (Issue #661). User-level — always populated, independently of current workspace."}},"type":"object","required":["memory_count","api_calls_today","api_calls_this_week","workspaces"],"title":"CurrentUsage","description":"Current usage statistics."},"DailyMemoryCount":{"properties":{"date":{"type":"string","title":"Date"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["date","count"],"title":"DailyMemoryCount","description":"Daily memory count item for timeline.\n\nIssue #275 Task 6: Memory timeline visualization."},"DailyStats":{"properties":{"date":{"type":"string","title":"Date","description":"Date in ISO format (YYYY-MM-DD)"},"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Number of requests on this date"}},"type":"object","required":["date","count"],"title":"DailyStats","description":"Daily usage statistics."},"DailyUsage":{"properties":{"date":{"type":"string","title":"Date","description":"Date in ISO format (YYYY-MM-DD)"},"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Number of requests on this date"}},"type":"object","required":["date","count"],"title":"DailyUsage","description":"Daily usage statistics."},"DailyUsageItem":{"properties":{"date":{"type":"string","title":"Date"},"api_calls":{"type":"integer","title":"Api Calls"},"unique_users":{"type":"integer","title":"Unique Users"}},"type":"object","required":["date","api_calls","unique_users"],"title":"DailyUsageItem","description":"Daily usage statistics item.\n\nIssue #249: Time-series usage data for context."},"DeviceAuthorizationResponse":{"properties":{"device_code":{"title":"Device Code"},"user_code":{"title":"User Code"},"verification_uri":{"title":"Verification Uri"},"verification_uri_complete":{"title":"Verification Uri Complete"},"expires_in":{"title":"Expires In"},"interval":{"title":"Interval"}},"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"title":"DeviceAuthorizationResponse","description":"Device Authorization Response (RFC 8628 Section 3.2)."},"DeviceConfirmRequest":{"properties":{"user_code":{"type":"string","maxLength":8,"minLength":8,"title":"User Code"},"approve":{"type":"boolean","title":"Approve","default":true}},"type":"object","required":["user_code"],"title":"DeviceConfirmRequest","description":"User consent decision for a pending device authorization."},"DeviceConfirmResponse":{"properties":{"status":{"title":"Status"},"user_code":{"title":"User Code"}},"type":"object","required":["status","user_code"],"title":"DeviceConfirmResponse","description":"Result of user consent decision."},"DeviceVerifyRequest":{"properties":{"user_code":{"type":"string","maxLength":8,"minLength":8,"title":"User Code"}},"type":"object","required":["user_code"],"title":"DeviceVerifyRequest","description":"Request to look up pending device authorization by user_code."},"DeviceVerifyResponse":{"properties":{"user_code":{"title":"User Code"},"client_name":{"title":"Client Name"},"scope":{"title":"Scope"},"expires_at":{"title":"Expires At"},"is_authorized":{"title":"Is Authorized"},"is_expired":{"title":"Is Expired"}},"type":"object","required":["user_code","client_name","scope","expires_at","is_authorized","is_expired"],"title":"DeviceVerifyResponse","description":"Pending device authorization info returned for the consent screen."},"DowngradeBlocker":{"properties":{"dimension":{"$ref":"#/components/schemas/DowngradeDimension"},"usage":{"type":"integer","title":"Usage"},"limit":{"type":"integer","title":"Limit"},"overage":{"type":"integer","title":"Overage"},"cleanup":{"$ref":"#/components/schemas/DowngradeCleanup"}},"type":"object","required":["dimension","usage","limit","overage","cleanup"],"title":"DowngradeBlocker","description":"One usage dimension that blocks (or requires cleanup before) a downgrade.\n\n``overage`` is how much must be shed to fit the target. For ``shared_contexts``\n(a tier feature, not a numeric cap) the limit is 0 and ``overage`` equals the\nfull shared-context count — all of them must be unshared. ``storage_bytes`` is\nin bytes and is the reducible figure of ``services/storage_counts.py`` (#1961):\nuploaded, not soft-deleted files that are workspace-scoped or in a live\ncontext — the same number the capacity lock uses, not the upload-quota\ncounter."},"DowngradeCleanup":{"type":"string","enum":["remove_members","delete_contexts","delete_memories","revoke_resource_tokens","unshare_contexts","remove_connectors","disable_sleep_contexts","delete_files"],"title":"DowngradeCleanup","description":"The destructive cleanup a blocker requires (stable wire contract)."},"DowngradeDimension":{"type":"string","enum":["members","contexts","memories","resource_tokens","shared_contexts","connectors","sleep_enabled_contexts","storage_bytes"],"title":"DowngradeDimension","description":"Usage dimensions that can block a downgrade (stable wire contract)."},"DowngradeEligibilityView":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"current_plan":{"type":"string","title":"Current Plan"},"targets":{"items":{"$ref":"#/components/schemas/TierDowngradeEligibility"},"type":"array","title":"Targets"}},"type":"object","required":["workspace_id","current_plan","targets"],"title":"DowngradeEligibilityView","description":"Usage-fit downgrade eligibility for every tier below the current one (#1123)."},"DuplicateMemoryInfo":{"properties":{"id":{"type":"string","title":"Id"},"summary":{"type":"string","title":"Summary"},"type":{"type":"string","title":"Type"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","summary","type","created_at"],"title":"DuplicateMemoryInfo","description":"Memory info for duplicate pair display."},"DuplicatePair":{"properties":{"memory_a":{"$ref":"#/components/schemas/DuplicateMemoryInfo"},"memory_b":{"$ref":"#/components/schemas/DuplicateMemoryInfo"},"similarity":{"type":"number","title":"Similarity"}},"type":"object","required":["memory_a","memory_b","similarity"],"title":"DuplicatePair","description":"A pair of similar memories."},"DuplicatesResponse":{"properties":{"pairs":{"items":{"$ref":"#/components/schemas/DuplicatePair"},"type":"array","title":"Pairs"},"total_pairs":{"type":"integer","title":"Total Pairs"},"threshold":{"type":"number","title":"Threshold"},"memories_scanned":{"type":"integer","title":"Memories Scanned"}},"type":"object","required":["pairs","total_pairs","threshold","memories_scanned"],"title":"DuplicatesResponse","description":"Response for duplicate detection endpoint."},"DynamicClientRegistrationRequest":{"properties":{"client_name":{"type":"string","maxLength":100,"minLength":1,"title":"Client Name","description":"Client name","default":"MCP Client"},"redirect_uris":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Redirect Uris","description":"Redirect URIs"},"grant_types":{"items":{"type":"string"},"type":"array","title":"Grant Types","description":"Grant types","default":["authorization_code","refresh_token"]},"response_types":{"items":{"type":"string"},"type":"array","title":"Response Types","description":"Response types","default":["code"]},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope","description":"Requested scopes (space-separated)"},"token_endpoint_auth_method":{"type":"string","title":"Token Endpoint Auth Method","description":"Token endpoint auth method","default":"none"}},"type":"object","required":["redirect_uris"],"title":"DynamicClientRegistrationRequest","description":"Dynamic Client Registration (DCR) request for MCP clients.\n\nSimplified schema for MCP client OAuth registration (ChatGPT, Claude,\nCursor, and loopback-only native clients such as Codex CLI).\nMCP clients use this for automatic registration."},"EmbeddingModelInfo":{"properties":{"name":{"type":"string","title":"Name"},"dimensions":{"type":"integer","title":"Dimensions"},"provider":{"type":"string","title":"Provider"},"available":{"type":"boolean","title":"Available"}},"type":"object","required":["name","dimensions","provider","available"],"title":"EmbeddingModelInfo","description":"Individual embedding model info."},"EmbeddingModelsResponse":{"properties":{"models":{"items":{"$ref":"#/components/schemas/EmbeddingModelInfo"},"type":"array","title":"Models"},"default_model":{"type":"string","title":"Default Model"}},"type":"object","required":["models","default_model"],"title":"EmbeddingModelsResponse","description":"Response for available embedding models."},"EmbeddingStatusResponse":{"properties":{"total":{"type":"integer","title":"Total"},"by_status":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Status"},"failed_memories":{"items":{"$ref":"#/components/schemas/FailedMemoryInfo"},"type":"array","title":"Failed Memories"}},"type":"object","required":["total","by_status","failed_memories"],"title":"EmbeddingStatusResponse","description":"Embedding queue status response."},"EndpointUsage":{"properties":{"endpoint":{"type":"string","title":"Endpoint","description":"API endpoint or MCP tool"},"count":{"type":"integer","minimum":0.0,"title":"Count","description":"Number of requests"},"percentage":{"type":"number","title":"Percentage","description":"Percentage of total requests"}},"type":"object","required":["endpoint","count","percentage"],"title":"EndpointUsage","description":"Usage by endpoint."},"ErasureConfirmRequest":{"properties":{"token":{"type":"string","title":"Token"},"password":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Password","description":"Required only for users with a password. Others omit."}},"type":"object","required":["token"],"title":"ErasureConfirmRequest","description":"Payload for POST /me/account/erasure-confirm."},"ErasureRequestCreateResponse":{"properties":{"request_id":{"title":"Request Id"},"status":{"title":"Status"},"requested_at":{"title":"Requested At"},"confirm_token":{"title":"Confirm Token","description":"One-time confirmation token, valid for 1 hour. **Populated only for users with a password** — they re-enter their password alongside this token at POST /me/account/erasure-confirm. **For users without a password this is null** and the token is delivered via email."}},"type":"object","required":["request_id","status","requested_at"],"title":"ErasureRequestCreateResponse","description":"Returned after creating a self-service erasure request.\n\nThe confirmation token is delivered through one of two channels\ndepending on whether the account has a password (Issue #469; since #1678\nan OAuth account that added a password counts as a password user):\n\n- **Users with a password**: ``confirm_token`` is populated in this\n  response. The user re-enters their password alongside this token at\n  ``POST /me/account/erasure-confirm`` (the password is the second\n  factor — the response token is the first).\n- **Users without a password**: ``confirm_token`` is ``None`` here. The token is\n  delivered via email to the user's account address as a one-time\n  confirm link. Email is the canonical second factor for OAuth, just\n  as the password re-prompt is for password-auth users — keeping the\n  raw token out of the response body removes a redundant copy that\n  would otherwise widen the disclosure surface (proxy access logs,\n  browser devtools, frontend error-reporters) once email actually\n  delivers it.\n\nThe raw token is stored in Redis under ``erasure_token:{token}`` with\na 1-hour TTL and is single-use regardless of delivery channel. The\nRedis key maps token → ``request_id`` only — it is NOT session-bound.\nConfirmation additionally requires the authenticated session user to\nmatch the erasure request's ``user_id`` (enforced by\n``confirm_self_service``), so a leaked token alone is insufficient\nwithout the matching session cookie.\n\nThe frontend SHOULD treat this token as sensitive and not log it."},"ErasureRequestStateResponse":{"properties":{"request_id":{"title":"Request Id"},"status":{"title":"Status"},"is_self_service":{"title":"Is Self Service"},"requested_at":{"title":"Requested At"},"confirmed_at":{"title":"Confirmed At"},"scheduled_for":{"title":"Scheduled For"},"started_at":{"title":"Started At"},"completed_at":{"title":"Completed At"},"cancelled_at":{"title":"Cancelled At"},"failure_reason":{"title":"Failure Reason"}},"type":"object","required":["request_id","status","is_self_service","requested_at"],"title":"ErasureRequestStateResponse","description":"Read-only view of an erasure request's lifecycle state."},"ExploreHint":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"reason":{"type":"string","enum":["top_result","high_centrality","unexplored_neighbor"],"title":"Reason"}},"type":"object","required":["memory_id","reason"],"title":"ExploreHint","description":"Hint suggesting a memory as a good seed for explore().\n\nIssue #216: Opt-in field to bridge recall → explore discovery."},"ExploreRequest":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id","description":"起点メモリID"},"depth":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Depth","description":"最大ホップ数","default":2},"relation_types":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Relation Types","description":"フィルタするリレーションタイプ"},"min_weight":{"type":"number","maximum":3.0,"minimum":0.0,"title":"Min Weight","description":"最小重み閾値（典型値: 0.02-0.05）","default":0.05}},"type":"object","required":["memory_id"],"title":"ExploreRequest","description":"Request schema for explore() API.\n\nExample:\n    {\n        \"memory_id\": \"uuid\",\n        \"depth\": 2,\n        \"relation_types\": [\"neural_association\"],\n        \"min_weight\": 0.05\n    }"},"ExploreResponse":{"properties":{"seed_memory":{"$ref":"#/components/schemas/MemoryResponse"},"related_memories":{"items":{"$ref":"#/components/schemas/RelatedMemoryResponse"},"type":"array","title":"Related Memories"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata","description":"探索統計（total_activated, returned, etc.）"}},"type":"object","required":["seed_memory","related_memories"],"title":"ExploreResponse","description":"Response schema for explore() API.\n\nReturns:\n    seed_memory: 起点メモリ\n    related_memories: 関連メモリーリスト\n    metadata: 探索メタデータ"},"ExportedContextMeta":{"properties":{"id":{"title":"Id"},"name":{"title":"Name"},"display_name":{"title":"Display Name"},"description":{"title":"Description"},"summary":{"title":"Summary"},"usage_guide":{"title":"Usage Guide"},"is_private":{"title":"Is Private"},"is_public":{"title":"Is Public"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["id","name","display_name","description","summary","usage_guide","is_private","is_public","created_at","updated_at"],"title":"ExportedContextMeta","description":"Context metadata block in a portability export (Issue #950)."},"ExportedMemory":{"properties":{"id":{"title":"Id"},"summary":{"title":"Summary"},"context_summary":{"title":"Context Summary"},"content":{"title":"Content"},"details":{"title":"Details"},"type":{"title":"Type"},"importance":{"title":"Importance"},"confidence":{"title":"Confidence"},"tags":{"title":"Tags"},"context":{"title":"Context"},"scope":{"title":"Scope"},"delivery_mode":{"title":"Delivery Mode"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"},"source_uri":{"title":"Source Uri"},"source_type":{"title":"Source Type"}},"type":"object","required":["id","summary","context_summary","content","details","type","importance","confidence","tags","context","scope","delivery_mode","created_at","updated_at"],"title":"ExportedMemory","description":"A single memory in a portability export (Issue #950).\n\nExcludes regenerable fields (vector / embedding status), DB-computed\ncolumns, internal owner/workspace identifiers, and soft-delete state —\nonly the user-meaningful, portable fields are emitted. ``details`` is\nincluded verbatim; note it may carry references (e.g. external blob\npointers) that will not resolve outside this deployment."},"ExportedSearchConfig":{"properties":{"semantic_weight":{"type":"number","title":"Semantic Weight"},"bm25_weight":{"type":"number","title":"Bm25 Weight"},"fetch_factor":{"type":"integer","title":"Fetch Factor"},"use_rerank":{"type":"boolean","title":"Use Rerank"},"reranker_provider":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reranker Provider"},"reranker_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reranker Model"},"embedding_model":{"type":"string","title":"Embedding Model"},"embedding_dimensions":{"type":"integer","title":"Embedding Dimensions"},"reinforce_enabled":{"type":"boolean","title":"Reinforce Enabled","default":true},"reinforce_max_boost":{"type":"number","title":"Reinforce Max Boost","default":0.15},"reinforce_require_host_arbitration":{"type":"boolean","title":"Reinforce Require Host Arbitration","default":false},"routing_mode":{"type":"string","title":"Routing Mode","default":"off"},"content_chunks_enabled":{"type":"boolean","title":"Content Chunks Enabled","default":false},"llm_selector_enabled":{"type":"boolean","title":"Llm Selector Enabled","default":false}},"type":"object","required":["semantic_weight","bm25_weight","fetch_factor","use_rerank","reranker_provider","reranker_model","embedding_model","embedding_dimensions"],"title":"ExportedSearchConfig","description":"Per-context search configuration in a portability export (Issue #950).\n\n#1207: reinforce settings are exported so an explicit opt-out survives the\nportability boundary — without them, re-creating an exported context would\nsilently pick up the new default-on. Defaults mirror the current ORM\ndefaults so pre-#1207 export documents (which lack these keys) still parse."},"ExternalKeyCreate":{"properties":{"key_name":{"type":"string","title":"Key Name"},"provider":{"type":"string","title":"Provider"},"value":{"type":"string","title":"Value"},"enabled":{"type":"boolean","title":"Enabled","default":true}},"type":"object","required":["key_name","provider","value"],"title":"ExternalKeyCreate","description":"Create external API key request."},"ExternalKeyListResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/ExternalKeyResponse"},"type":"array","title":"Keys"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["keys","total"],"title":"ExternalKeyListResponse","description":"External API keys list response."},"ExternalKeyResponse":{"properties":{"key_name":{"type":"string","title":"Key Name"},"provider":{"type":"string","title":"Provider"},"masked_value":{"type":"string","title":"Masked Value"},"user_id":{"type":"string","title":"User Id"},"enabled":{"type":"boolean","title":"Enabled"},"is_protected":{"type":"boolean","title":"Is Protected"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"type":"object","required":["key_name","provider","masked_value","user_id","enabled","is_protected","created_at","updated_at"],"title":"ExternalKeyResponse","description":"External API key response (masked).\n\nNo ``id``: external keys are addressed by ``key_name`` (#1008 dropped the\ninteger primary key from the response)."},"ExternalKeyToggle":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"ExternalKeyToggle","description":"Toggle enabled/disabled state (Issue #105)."},"ExternalKeyUpdate":{"properties":{"value":{"type":"string","title":"Value"}},"type":"object","required":["value"],"title":"ExternalKeyUpdate","description":"Update external API key request."},"FailedMemoryInfo":{"properties":{"id":{"type":"string","title":"Id"},"summary":{"type":"string","title":"Summary"},"embedding_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Embedding Error"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated At"},"embedding_attempted_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Embedding Attempted At"}},"type":"object","required":["id","summary","embedding_error","created_at","updated_at"],"title":"FailedMemoryInfo","description":"Info about a failed embedding memory."},"FeedbackRequest":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id","description":"The recalled memory being rated"},"helpful":{"type":"boolean","title":"Helpful","description":"Was this memory useful for the query?"},"query":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Query","description":"The recall query this feedback is about (optional)"},"note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Note","description":"Optional free-text note (e.g. why it was wrong). Max 2000 chars."}},"type":"object","required":["memory_id","helpful"],"title":"FeedbackRequest","description":"Body for recording retrieval feedback."},"FeedbackResponse":{"properties":{"feedback_id":{"type":"string","format":"uuid","title":"Feedback Id"},"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"helpful":{"type":"boolean","title":"Helpful"}},"type":"object","required":["feedback_id","memory_id","helpful"],"title":"FeedbackResponse"},"FieldDefinition":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","description":"Field name in JSONB payload"},"type":{"type":"string","pattern":"^(text|number|boolean|date|array|object)$","title":"Type","description":"Field data type"},"description":{"type":"string","maxLength":500,"minLength":1,"title":"Description","description":"Human-readable description"},"classification":{"type":"string","pattern":"^(public|internal|pii|confidential)$","title":"Classification","description":"Data classification for access control","default":"public"},"index_hint":{"type":"string","maxLength":100,"title":"Index Hint","description":"Indexing hints: fulltext, vector, sort, facet, metadata","default":""},"unit":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Unit","description":"Unit of measurement (e.g., JPY, kg)"},"enum_values":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Enum Values","description":"Allowed values for categorical fields"},"example":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Example","description":"Example value"},"required":{"type":"boolean","title":"Required","description":"Whether field is required","default":false}},"type":"object","required":["name","type","description"],"title":"FieldDefinition","description":"Field metadata definition."},"FileConfirmRequest":{"properties":{"sha256":{"type":"string","maxLength":64,"minLength":64,"pattern":"^[0-9a-fA-F]{64}$","title":"Sha256"}},"type":"object","required":["sha256"],"title":"FileConfirmRequest","description":"Body for ``POST /api/v1/files/{file_id}/confirm``."},"FileDownloadUrlOut":{"properties":{"download_url":{"title":"Download Url"}},"type":"object","required":["download_url"],"title":"FileDownloadUrlOut"},"FileObjectOut":{"properties":{"id":{"title":"Id"},"workspace_id":{"title":"Workspace Id"},"context_id":{"title":"Context Id"},"filename":{"title":"Filename"},"content_type":{"title":"Content Type"},"size_bytes":{"title":"Size Bytes"},"sha256":{"title":"Sha256"},"status":{"title":"Status"},"created_at":{"title":"Created At"},"uploaded_at":{"title":"Uploaded At"}},"type":"object","required":["id","workspace_id","filename","content_type","size_bytes","sha256","status","created_at"],"title":"FileObjectOut","description":"Subset of ``FileObject`` exposed to clients.\n\n``from_attributes=True`` lets us pass the ORM row directly to\n``model_validate`` — no per-field copy needed."},"FileReserveRequest":{"properties":{"workspace_id":{"type":"string","format":"uuid","title":"Workspace Id"},"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id","description":"Optional owning context (Issue #1136). When set, the caller must have write access to it and the file inherits that context's ACL for all later read/write/list access. Omit for a workspace-scoped file (legacy behaviour: any workspace viewer can download it)."},"filename":{"type":"string","maxLength":512,"minLength":1,"title":"Filename"},"content_type":{"type":"string","maxLength":255,"minLength":1,"title":"Content Type"},"size_bytes":{"type":"integer","exclusiveMinimum":0.0,"title":"Size Bytes"},"sha256":{"type":"string","maxLength":64,"minLength":64,"pattern":"^[0-9a-fA-F]{64}$","title":"Sha256","description":"Hex sha256 of the bytes the client will PUT. Accepts upper- or lower-case; the service canonicalizes to lowercase before any storage / dedup / signing use."}},"type":"object","required":["workspace_id","filename","content_type","size_bytes","sha256"],"title":"FileReserveRequest","description":"Body for ``POST /api/v1/files/reserve``."},"FileReserveResponse":{"properties":{"file_id":{"title":"File Id"},"upload_url":{"title":"Upload Url"},"expires_at":{"title":"Expires At"}},"type":"object","required":["file_id","upload_url","expires_at"],"title":"FileReserveResponse"},"FoldedVersions":{"properties":{"count":{"title":"Count"},"memory_ids":{"title":"Memory Ids"}},"type":"object","required":["count","memory_ids"],"title":"FoldedVersions","description":"#2123: older versions of one fact folded under the newest one in recall.\n\nA version is folded only when the deployment's judge verified the pair\n(``supersede_candidate.verified``) and both memories were in the same\nrecall's candidate pool; ``include_superseded=true`` turns folding off."},"ForceTransferOwnershipRequest":{"properties":{"target_user_id":{"type":"string","maxLength":255,"minLength":1,"title":"Target User Id"},"reason":{"type":"string","maxLength":1000,"minLength":3,"title":"Reason","description":"Why ownership is being force-transferred (required, audited)."}},"type":"object","required":["target_user_id","reason"],"title":"ForceTransferOwnershipRequest","description":"Break-glass force-transfer request (#1101).\n\n``reason`` is REQUIRED (non-empty) — a privileged ownership override with no\njustification is a red flag; it is recorded verbatim in the mandatory audit."},"ForgetBulkRequest":{"properties":{"context_id":{"type":"string","format":"uuid","title":"Context Id","description":"The context to delete from"},"created_before":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created Before","description":"Only memories created before this instant (exclusive)"},"created_after":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created After","description":"Only memories created at or after this instant (inclusive)"},"type":{"anyOf":[{"type":"string","maxLength":50},{"type":"null"}],"title":"Type","description":"Only memories of this type"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Tags","description":"Only memories carrying ANY of these tags"},"all":{"type":"boolean","title":"All","description":"Match every memory in the context (no filter)","default":false},"dry_run":{"type":"boolean","title":"Dry Run","description":"Count the matches without deleting","default":true},"cursor":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Cursor","description":"Resume point: the previous response's next_cursor, so rows this caller may not delete are not rescanned. Ignored on a dry run."}},"type":"object","required":["context_id"],"title":"ForgetBulkRequest","description":"Request schema for ``POST /memory/forget-bulk`` (#1941).\n\nDeletes every live memory in ONE context matching ALL the given filters.\nAt least one filter, or an explicit ``all: true``, is required so an empty\nbody can never wipe a context. ``dry_run`` defaults to true: the caller\nsees the count before anything is deleted."},"ForgetBulkResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"dry_run":{"type":"boolean","title":"Dry Run"},"matched":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Matched"},"deleted":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Deleted"},"remaining":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Remaining"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["dry_run"],"title":"ForgetBulkResponse","description":"Response schema for ``POST /memory/forget-bulk`` (#1941).\n\n``matched`` (the full count) on a dry run; ``deleted``, ``remaining`` and\n``next_cursor`` otherwise — one request deletes at most 2,000 memories;\n``remaining: true`` means matching memories are left: repeat the request\nwith ``cursor: next_cursor``."},"ForgetRequest":{"properties":{"memory_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Memory Id","description":"削除するメモリID"},"query":{"anyOf":[{"type":"string","maxLength":8000},{"type":"null"}],"title":"Query","description":"削除する検索クエリ"},"k":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"K","description":"削除する結果数（query指定時）","default":10}},"type":"object","title":"ForgetRequest","description":"Request schema for forget() API."},"ForgetResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"deleted_count":{"type":"integer","title":"Deleted Count"},"memory_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Memory Ids"}},"type":"object","required":["deleted_count","memory_ids"],"title":"ForgetResponse","description":"Response schema for forget() API."},"GraphDataResponse":{"properties":{"nodes":{"items":{"$ref":"#/components/schemas/GraphNode"},"type":"array","title":"Nodes"},"edges":{"items":{"$ref":"#/components/schemas/GraphEdge"},"type":"array","title":"Edges"},"stats":{"additionalProperties":true,"type":"object","title":"Stats"}},"type":"object","required":["nodes","edges","stats"],"title":"GraphDataResponse","description":"Graph data for visualization."},"GraphEdge":{"properties":{"source":{"type":"string","title":"Source"},"target":{"type":"string","title":"Target"},"weight":{"type":"number","title":"Weight"},"type":{"type":"string","title":"Type"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence"}},"type":"object","required":["source","target","weight","type"],"title":"GraphEdge","description":"Graph edge for visualization.\n\n`created_at` and `confidence` are surfaced for downstream UI consumers\n(#430 — graph edge metadata overlay, future #435). They were already\non the underlying `NeuralMemoryEdge` row but previously stripped at\nthis response model. Both stay optional so existing API clients that\ndon't read the new fields continue to deserialize unchanged."},"GraphNode":{"properties":{"id":{"type":"string","title":"Id"},"summary":{"type":"string","title":"Summary"},"type":{"type":"string","title":"Type"},"importance":{"type":"number","title":"Importance"},"degree":{"type":"integer","title":"Degree"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","summary","type","importance","degree"],"title":"GraphNode","description":"Graph node for visualization."},"GraphStats":{"properties":{"total_nodes":{"type":"integer","title":"Total Nodes"},"total_edges":{"type":"integer","title":"Total Edges"},"avg_edge_weight":{"type":"number","title":"Avg Edge Weight"},"max_edge_weight":{"type":"number","title":"Max Edge Weight"},"min_edge_weight":{"type":"number","title":"Min Edge Weight"},"density":{"type":"number","title":"Density"},"top_connections":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Top Connections"},"recent_edges":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Recent Edges"}},"type":"object","required":["total_nodes","total_edges","avg_edge_weight","max_edge_weight","min_edge_weight","density","top_connections","recent_edges"],"title":"GraphStats","description":"Graph statistics response."},"GraphStatsResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"stats":{"$ref":"#/components/schemas/GraphStats"},"last_updated":{"type":"string","title":"Last Updated"}},"type":"object","required":["user_id","stats","last_updated"],"title":"GraphStatsResponse","description":"Graph stats API response."},"GuardrailItem":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"context_summary":{"title":"Context Summary"},"type":{"title":"Type"},"importance":{"title":"Importance"},"delivery_mode":{"title":"Delivery Mode"},"tool_trigger":{"title":"Tool Trigger"},"source_type":{"title":"Source Type"},"authored_by_caller":{"title":"Authored By Caller"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["memory_id","summary","type","importance","delivery_mode","source_type","authored_by_caller","created_at","updated_at"],"title":"GuardrailItem","description":"One served guardrail-set entry (``load_guardrails``; docs/mcp-tools.md § Tool guardrails).\n\nOne shape for both lists. ``summary`` (L1) is the text a client hook\ninjects; ``context_summary`` (L2) is present on pinned items only and is\n``None`` on tool-triggered items — the hook never injects it, and a\nrate-limit-exempt read with ``cap`` up to 1000 must not ship L2 text no\nconsumer reads. Never L3 ``content``, never ``details`` beyond the\nnormalized ``tool_trigger`` object (``None`` on pinned items). Provenance:\n``source_type`` and ``authored_by_caller`` (``row.user_id == caller``, no\nid exposure) let a client label a foreign-authored guardrail.\n``updated_at`` falls back to ``created_at`` (nullable column)."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HostFeedbackRequest":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id","description":"The recalled memory being rated"},"helpful":{"type":"boolean","title":"Helpful","description":"Independent host verdict"},"query":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Query"},"verdict_source":{"type":"string","enum":["objective_check","trusted_host_check","hitl_approval"],"title":"Verdict Source"},"verdict_reference":{"type":"string","maxLength":1024,"title":"Verdict Reference"},"experiment_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Experiment Id"},"note":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Note"}},"type":"object","required":["memory_id","helpful","verdict_source","verdict_reference"],"title":"HostFeedbackRequest","description":"Trusted operator verdict; provenance is intentionally not caller-settable."},"IdentityLinkStatusResponse":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"IdentityLinkStatusResponse","description":"Returned by the link and unlink endpoints on success."},"IdentityLinkTarget":{"properties":{"user_id":{"type":"string","maxLength":255,"minLength":1,"title":"User Id"}},"type":"object","required":["user_id"],"title":"IdentityLinkTarget","description":"Body for linking or unlinking an account."},"IdentityLinksResponse":{"properties":{"linked":{"items":{"$ref":"#/components/schemas/LinkedIdentityItem"},"type":"array","title":"Linked"},"linkable":{"items":{"$ref":"#/components/schemas/LinkableIdentityItem"},"type":"array","title":"Linkable"},"signed_in_recently":{"type":"boolean","title":"Signed In Recently","default":false},"sign_in_window_minutes":{"type":"integer","title":"Sign In Window Minutes","default":10},"providers":{"items":{"type":"string"},"type":"array","title":"Providers","default":[]}},"type":"object","required":["linked","linkable"],"title":"IdentityLinksResponse","description":"The session user's link set, and what this session could add to it.\n\n``signed_in_recently``: the session user itself proved its credential here\nwithin the link window (#1818), as for a linkable account."},"IndexerState":{"properties":{"job_status":{"type":"string","enum":["idle","queued","running","failed"],"title":"Job Status"},"last_run_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Run At","description":"ISO-8601 UTC timestamp of the most recent run"},"next_run_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Run At","description":"ISO-8601 UTC timestamp of the next scheduled run"},"active_version":{"type":"integer","title":"Active Version","description":"Active Qdrant collection version (blue/green); used for rollout coordination."},"last_offset":{"type":"integer","title":"Last Offset","description":"Highest resource_events.id processed so far. Monotonically increasing; compare against ResourceEvent.id to estimate lag."},"lag_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Lag Seconds","description":"Server-computed (now - last_run_at). ``None`` when the indexer has never run. The UI uses this for health thresholds only — display a human relative time from ``last_run_at`` directly to avoid value drift while the tab is open."},"metrics":{"$ref":"#/components/schemas/IndexerStateMetrics"}},"type":"object","required":["job_status","active_version","last_offset","metrics"],"title":"IndexerState","description":"Indexer state snapshot for one resource/context."},"IndexerStateMetrics":{"properties":{"applied_upserts":{"type":"integer","title":"Applied Upserts","description":"Upsert events applied in the last run","default":0},"applied_deletes":{"type":"integer","title":"Applied Deletes","description":"Delete events applied in the last run","default":0},"errors":{"type":"integer","title":"Errors","description":"Errors observed in the last run","default":0},"skipped_reason":{"anyOf":[{"type":"string","enum":["no_pending_events","schema_not_found","context_not_found","empty_valid_points","resource_entity_missing","memories_per_day_exceeded","plan_suspended","memory_limit_exceeded"]},{"type":"null"}],"title":"Skipped Reason","description":"Present only when the last run was skipped. Surfacing a stale reason after a successful run is a UI bug we explicitly avoid."}},"type":"object","title":"IndexerStateMetrics","description":"Per-run indexer metrics, flattened from the JSONB column."},"IndexerStatusResponse":{"properties":{"resource_id":{"type":"string","title":"Resource Id","description":"Echo of the requested slug"},"state":{"anyOf":[{"$ref":"#/components/schemas/IndexerState"},{"type":"null"}],"description":"Indexer state row, or ``None`` when the indexer has never run against this resource/context. The 2-value model is intentional so the UI branches on ``state is None`` rather than inferring emptiness from individual field values."},"recent_events":{"items":{"$ref":"#/components/schemas/ResourceEventItem"},"type":"array","title":"Recent Events","description":"Latest ingest events, newest first. Capped server-side at 5 — there is no client-tunable limit on this endpoint by design; a cursor-paginated timeline belongs on a dedicated endpoint."}},"type":"object","required":["resource_id"],"title":"IndexerStatusResponse","description":"Response body for ``GET /api/v1/resources/{resource_id}/indexer-status``."},"LinkProviderRequest":{"properties":{"provider":{"type":"string","enum":["google","github"],"title":"Provider"}},"type":"object","required":["provider"],"title":"LinkProviderRequest","description":"Body for POST /me/account/link-provider."},"LinkProviderResponse":{"properties":{"authorization_url":{"type":"string","title":"Authorization Url"},"state":{"type":"string","title":"State"}},"type":"object","required":["authorization_url","state"],"title":"LinkProviderResponse","description":"Frontend redirects ``window.location`` to ``authorization_url``.\n\nMirrors ``me_oauth.RefreshOAuthResponse``: the OAuth round-trip itself\nis the fresh re-auth, so no password is ever prompted (edge case 1 — the\nlocked re-auth contract for OAuth-only users)."},"LinkableIdentityItem":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"signed_in_recently":{"type":"boolean","title":"Signed In Recently","default":false},"providers":{"items":{"type":"string"},"type":"array","title":"Providers","default":[]}},"type":"object","required":["user_id"],"title":"LinkableIdentityItem","description":"An account signed in on this browser session that is not linked yet.\n\n``signed_in_recently``: it proved its credential here within the link\nwindow (#1818: a password sign-in, or a Google sign-in with a recent\n``auth_time``), so it can be linked now. An OAuth sign-in alone does not\ncount unless the operator allows it."},"LinkedIdentityItem":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"linked_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linked At"}},"type":"object","required":["user_id"],"title":"LinkedIdentityItem","description":"An account counted as the same owner as the session user.\n\n``linked_at`` is an ISO 8601 string with an explicit ``Z`` (``to_utc_iso``)."},"LinkedMemoryRef":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"type":{"title":"Type"},"importance":{"title":"Importance"},"weight":{"title":"Weight"},"created_at":{"title":"Created At"}},"type":"object","required":["memory_id","summary","importance","weight","created_at"],"title":"LinkedMemoryRef","description":"A single declared_link reference surfaced in ReferenceResponse.\n\nIssue #440: outgoing_links / incoming_links items. Edge invariant\n(`_validate_edge_context_invariant`) guarantees both endpoints share\nthe same (workspace_id, context_id) as the source memory, so a\nworkspace-scoped permission check on the source covers these too.\nThe bulk re-scope in MemoryService.reference() is defense-in-depth\nagainst soft-deleted or invariant-violating rows."},"LinkedProvider":{"properties":{"provider":{"type":"string","title":"Provider"},"linked_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Linked At"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"}},"type":"object","required":["provider"],"title":"LinkedProvider","description":"One linked OAuth identity, as surfaced to the profile UI.\n\n``linked_at`` / ``last_used_at`` are pre-serialized to ISO 8601 strings\nwith an explicit ``Z`` suffix via ``to_utc_iso`` (the source columns are\nnaive UTC ``TIMESTAMP WITHOUT TIME ZONE``), so JS clients don't reparse\nthem as local time."},"LoadGuardrailsRequest":{"properties":{"context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Id","description":"Context UUID whose guardrail set to load"},"cap":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Cap","description":"Optional override for the max tool-triggered memories returned (bounded)"}},"type":"object","title":"LoadGuardrailsRequest","description":"Request for the deterministic guardrail-set read (``POST /memory/guardrails``).\n\n``cap`` bounds the tool-triggered list only (``settings.guardrail_load_cap``\ndefault); the pinned list is bounded by ``settings.pinned_load_cap`` so a\nlarge pinned set can never crowd guardrails out of a capped response."},"LoadGuardrailsResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"format":{"type":"integer","title":"Format"},"version":{"type":"string","title":"Version"},"pinned":{"items":{"$ref":"#/components/schemas/GuardrailItem"},"type":"array","title":"Pinned"},"tool_triggered":{"items":{"$ref":"#/components/schemas/GuardrailItem"},"type":"array","title":"Tool Triggered"},"total_available":{"type":"integer","title":"Total Available"},"truncated":{"type":"boolean","title":"Truncated"},"cap":{"type":"integer","title":"Cap"},"pinned_cap":{"type":"integer","title":"Pinned Cap"},"pinned_total_available":{"type":"integer","title":"Pinned Total Available"},"pinned_truncated":{"type":"boolean","title":"Pinned Truncated"},"tool_triggered_total_available":{"type":"integer","title":"Tool Triggered Total Available"},"tool_triggered_truncated":{"type":"boolean","title":"Tool Triggered Truncated"}},"type":"object","required":["format","version","pinned","tool_triggered","total_available","truncated","cap","pinned_cap","pinned_total_available","pinned_truncated","tool_triggered_total_available","tool_triggered_truncated"],"title":"LoadGuardrailsResponse","description":"Response for the deterministic guardrail-set read (docs/mcp-tools.md § Tool guardrails).\n\nTwo independently capped lanes: ``pinned`` (``delivery_mode='always'``,\ntrusted tier, ``pinned_load_cap``) and ``tool_triggered``\n(``details.tool_trigger`` present, trusted tier, ``cap``). A memory that is\nboth appears in both lists (clients dedupe by ``memory_id``). Each list is\nin the canonical order ``importance DESC, created_at ASC, id ASC``.\n\n``total_available`` / ``truncated`` / ``cap`` keep the single top-level\nmeaning (sum / either-lane / the tool-triggered cap); the per-lane fields\nsay which protection is incomplete. ``format`` is the shared cache/payload\nformat version (additive fields never bump it); ``version`` is the\nper-credential hash of the served entries, opaque to clients."},"LoadPinnedRequest":{"properties":{"context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Id","description":"Context UUID whose pinned memories to load"},"cap":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Cap","description":"Optional override for the max returned (bounded)"}},"type":"object","title":"LoadPinnedRequest","description":"Request for the deterministic always-load read path (Issue #886).\n\n``context_id`` selects which context's always-load set to return (the set\nis per-context). ``cap`` optionally overrides ``settings.pinned_load_cap``."},"LoadPinnedResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"memories":{"items":{"$ref":"#/components/schemas/PinnedMemoryItem"},"type":"array","title":"Memories"},"total_available":{"type":"integer","title":"Total Available"},"truncated":{"type":"boolean","title":"Truncated"},"cap":{"type":"integer","title":"Cap"}},"type":"object","required":["memories","total_available","truncated","cap"],"title":"LoadPinnedResponse","description":"Response for the deterministic always-load read path (Issue #886).\n\n``memories`` is the complete, unranked, ordered set up to ``cap``. When the\ncontext holds more pinned memories than ``cap``, ``truncated`` is true and\n``total_available`` reports the real count — the set is never silently cut."},"MCPStatusResponse":{"properties":{"status":{"type":"string","title":"Status"},"active_sessions":{"type":"integer","title":"Active Sessions"},"total_tools":{"type":"integer","title":"Total Tools"},"tools_available":{"items":{"type":"string"},"type":"array","title":"Tools Available"},"last_activity":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Activity"}},"type":"object","required":["status","active_sessions","total_tools","tools_available","last_activity"],"title":"MCPStatusResponse","description":"MCP server status response."},"MCPToolInfo":{"properties":{"name":{"type":"string","title":"Name"},"description":{"type":"string","title":"Description"},"input_schema":{"additionalProperties":true,"type":"object","title":"Input Schema"}},"type":"object","required":["name","description","input_schema"],"title":"MCPToolInfo","description":"MCP Tool information."},"MCPToolsResponse":{"properties":{"tools":{"items":{"$ref":"#/components/schemas/MCPToolInfo"},"type":"array","title":"Tools"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["tools","total"],"title":"MCPToolsResponse","description":"MCP tools list response."},"MemberAPIKeyResponse":{"properties":{"id":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","title":"Id"},"name":{"type":"string","title":"Name"},"key_prefix":{"type":"string","title":"Key Prefix"},"plaintext_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plaintext Key"},"is_visible":{"type":"boolean","title":"Is Visible"},"visibility_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility Expires At"},"created_at":{"type":"string","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoked At"},"expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Expires At"},"bound_context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bound Context Id"}},"type":"object","required":["id","name","key_prefix","plaintext_key","is_visible","visibility_expires_at","created_at","revoked_at"],"title":"MemberAPIKeyResponse","description":"Response for member's API key (Zero-knowledge model).\n\nMigration 034: Member-scoped credentials.\nIssue #626: Optional public-context binding."},"MemberCredentialsResponse":{"properties":{"api_keys":{"items":{"$ref":"#/components/schemas/MemberAPIKeyResponse"},"type":"array","title":"Api Keys"},"target_user_role":{"type":"string","title":"Target User Role"}},"type":"object","required":["api_keys","target_user_role"],"title":"MemberCredentialsResponse","description":"Response for member credentials (API Keys).\n\nMigration 034: Member-scoped credentials.\nNote: OAuth Apps managed via /oauth/clients API."},"MemberUsageEntry":{"properties":{"user_id":{"type":"string","title":"User Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"memory_count":{"type":"integer","title":"Memory Count"},"api_calls_today":{"type":"integer","title":"Api Calls Today"},"api_calls_week":{"type":"integer","title":"Api Calls Week"}},"type":"object","required":["user_id","name","email","memory_count","api_calls_today","api_calls_week"],"title":"MemberUsageEntry","description":"Usage stats for a single workspace member."},"MemberUsageResponse":{"properties":{"members":{"items":{"$ref":"#/components/schemas/MemberUsageEntry"},"type":"array","title":"Members"},"total_members":{"type":"integer","title":"Total Members"}},"type":"object","required":["members","total_members"],"title":"MemberUsageResponse","description":"Per-member usage breakdown for workspace."},"MemoryHealthBreakdownResponse":{"properties":{"generated_at":{"type":"string","title":"Generated At"},"overall_status":{"type":"string","enum":["ok","warn","fail"],"title":"Overall Status"},"contexts":{"items":{"$ref":"#/components/schemas/MemoryHealthContextEntry"},"type":"array","title":"Contexts"}},"type":"object","required":["generated_at","overall_status","contexts"],"title":"MemoryHealthBreakdownResponse","description":"200 response for GET /admin/memory-health (no context_id)."},"MemoryHealthContextEntry":{"properties":{"context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"overall_status":{"type":"string","enum":["ok","warn","fail"],"title":"Overall Status"},"sections":{"additionalProperties":{"type":"string","enum":["ok","warn","fail"]},"type":"object","title":"Sections"}},"type":"object","required":["context_id","name","overall_status","sections"],"title":"MemoryHealthContextEntry","description":"One breakdown row: a single context's graded statuses.\n\nAttributes:\n    context_id: The context UUID, or null for the unattributed bucket\n        (signals recorded without a context).\n    name: Context display name; null for the unattributed bucket.\n    overall_status: Worst section status for this context.\n    sections: Per-section status only — metrics live in the detail view."},"MemoryHealthDetailResponse":{"properties":{"generated_at":{"type":"string","title":"Generated At"},"context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Id"},"context_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Name"},"overall_status":{"type":"string","enum":["ok","warn","fail"],"title":"Overall Status"},"sections":{"additionalProperties":{"$ref":"#/components/schemas/MemoryHealthSection"},"type":"object","title":"Sections"}},"type":"object","required":["generated_at","context_id","context_name","overall_status","sections"],"title":"MemoryHealthDetailResponse","description":"200 response for GET /admin/memory-health?context_id=..."},"MemoryHealthNote":{"properties":{"code":{"type":"string","title":"Code"},"params":{"additionalProperties":true,"type":"object","title":"Params"}},"type":"object","required":["code"],"title":"MemoryHealthNote","description":"One structured section note (#1225).\n\nAttributes:\n    code: Stable note code the frontend maps to a localized string\n        (the code → rationale mapping lives in\n        docs/ops/memory-health-report.md).\n    params: Interpolation values for the localized message."},"MemoryHealthSection":{"properties":{"status":{"type":"string","enum":["ok","warn","fail"],"title":"Status"},"metrics":{"additionalProperties":true,"type":"object","title":"Metrics"},"notes":{"items":{"$ref":"#/components/schemas/MemoryHealthNote"},"type":"array","title":"Notes"}},"type":"object","required":["status","metrics","notes"],"title":"MemoryHealthSection","description":"One graded health section.\n\nAttributes:\n    status: ok | warn | fail (fail only on deterministic facts).\n    metrics: Label-free numeric signals backing the grade.\n    notes: Structured explanations for every non-ok contribution, plus\n        informational notes on ok sections (e.g. an idle store)."},"MemoryListItem":{"properties":{"id":{"type":"string","title":"Id"},"summary":{"type":"string","title":"Summary"},"type":{"type":"string","title":"Type"},"scope":{"type":"string","title":"Scope"},"importance":{"type":"number","title":"Importance"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"},"location":{"anyOf":[{"$ref":"#/components/schemas/MemoryListItemLocation"},{"type":"null"}]}},"type":"object","required":["id","summary","type","scope","importance","created_at","updated_at"],"title":"MemoryListItem","description":"Memory list item."},"MemoryListItemLocation":{"properties":{"lat":{"type":"number","title":"Lat"},"lon":{"type":"number","title":"Lon"}},"type":"object","required":["lat","lon"],"title":"MemoryListItemLocation","description":"WHERE-axis coordinates of a list item (#1334), sourced from the\n``location_lat`` / ``location_lon`` generated columns."},"MemoryListResponse":{"properties":{"memories":{"items":{"$ref":"#/components/schemas/MemoryListItem"},"type":"array","title":"Memories"},"total":{"type":"integer","title":"Total"},"has_more":{"type":"boolean","title":"Has More"}},"type":"object","required":["memories","total","has_more"],"title":"MemoryListResponse","description":"Memory list response."},"MemoryResponse":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"context_summary":{"title":"Context Summary"},"type":{"title":"Type"},"importance":{"title":"Importance"},"scope":{"title":"Scope"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"},"client":{"title":"Client"},"tags":{"title":"Tags"},"context":{"title":"Context"},"score":{"title":"Score","description":"検索スコア（recall時のみ）"},"source_uri":{"title":"Source Uri"},"source_type":{"title":"Source Type"},"superseded_by":{"title":"Superseded By"},"contradicts":{"title":"Contradicts"},"supersede_candidate":{},"folded":{},"version":{"title":"Version"},"content":{"title":"Content"},"content_truncated":{"title":"Content Truncated"},"content_next_offset":{"title":"Content Next Offset"},"spans":{"title":"Spans"}},"type":"object","required":["memory_id","summary","context_summary","type","importance","scope","created_at","client","tags","context"],"title":"MemoryResponse","description":"Response schema for single memory."},"MemoryRevisionItem":{"properties":{"version":{"title":"Version"},"kind":{"title":"Kind"},"at":{"title":"At"},"changed_fields":{"title":"Changed Fields"},"writer":{"title":"Writer"},"by_you":{"title":"By You"},"agent_id":{"title":"Agent Id"},"session_id":{"title":"Session Id"}},"type":"object","required":["version","kind","at","changed_fields","writer","by_you"],"title":"MemoryRevisionItem","description":"One event of a memory's revision log (#2043).\n\nNo member identity: ``by_you`` says whether the caller (or an account\nlinked to them) wrote it; ``agent_id`` / ``session_id`` show only on the\ncaller's own events."},"MemoryRevisionsResponse":{"properties":{"memory_id":{"title":"Memory Id"},"current_version":{"title":"Current Version"},"revisions":{"title":"Revisions"},"has_more":{"title":"Has More"},"next_cursor":{"title":"Next Cursor"}},"type":"object","required":["memory_id","current_version","revisions","has_more"],"title":"MemoryRevisionsResponse","description":"A page of a memory's revision log, newest first (#2043)."},"MemoryStatItem":{"properties":{"id":{"type":"string","title":"Id"},"summary":{"type":"string","title":"Summary"},"type":{"type":"string","title":"Type"},"importance":{"type":"number","title":"Importance"},"scope":{"type":"string","title":"Scope"},"access_count":{"type":"integer","title":"Access Count"},"reference_count":{"type":"integer","title":"Reference Count"},"last_used_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Used At"},"embedding_status":{"type":"string","title":"Embedding Status"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["id","summary","type","importance","scope","access_count","reference_count","last_used_at","embedding_status","created_at"],"title":"MemoryStatItem","description":"Per-memory usage statistics."},"MemoryStatsResponse":{"properties":{"total_count":{"type":"integer","title":"Total Count","description":"Total number of memories (excluding deleted)"},"working_count":{"type":"integer","title":"Working Count","description":"Number of working scope memories"},"persistent_count":{"type":"integer","title":"Persistent Count","description":"Number of persistent scope memories"},"by_type":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Type","description":"Memory count breakdown by type"},"by_importance":{"additionalProperties":{"type":"integer"},"type":"object","title":"By Importance","description":"Memory count breakdown by importance level"},"recent_activity":{"type":"integer","title":"Recent Activity","description":"Recent memories count (time window varies: 24h for REST, 7d for MCP)","default":0}},"type":"object","required":["total_count","working_count","persistent_count"],"title":"MemoryStatsResponse","description":"Memory statistics response schema (Issue #84).\n\nUsed by both MCP get_stats tool and REST API /memory/stats endpoint."},"MemoryTimelineResponse":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"workspace_name":{"type":"string","title":"Workspace Name"},"daily_counts":{"items":{"$ref":"#/components/schemas/DailyMemoryCount"},"type":"array","title":"Daily Counts"},"memories_created_in_period":{"type":"integer","title":"Memories Created In Period"},"period_start":{"type":"string","title":"Period Start"},"period_end":{"type":"string","title":"Period End"}},"type":"object","required":["workspace_id","workspace_name","daily_counts","memories_created_in_period","period_start","period_end"],"title":"MemoryTimelineResponse","description":"Response model for workspace memory timeline.\n\nIssue #275 Task 6: GET /api/v1/workspaces/{workspace_id}/memory-timeline"},"MemoryUsageStatsResponse":{"properties":{"memories":{"items":{"$ref":"#/components/schemas/MemoryStatItem"},"type":"array","title":"Memories"},"total":{"type":"integer","title":"Total"},"sort_by":{"type":"string","title":"Sort By"},"sort_order":{"type":"string","title":"Sort Order"}},"type":"object","required":["memories","total","sort_by","sort_order"],"title":"MemoryUsageStatsResponse","description":"Response for per-memory stats endpoint."},"MfaVerifyRequest":{"properties":{"mfa_session_token":{"type":"string","title":"Mfa Session Token"},"totp_code":{"type":"string","title":"Totp Code"}},"type":"object","required":["mfa_session_token","totp_code"],"title":"MfaVerifyRequest","description":"MFA verification request."},"NeuralConfigItem":{"properties":{"key":{"title":"Key"},"value":{"title":"Value"},"value_type":{"title":"Value Type"},"category":{"title":"Category"},"description":{"title":"Description"},"min_value":{"title":"Min Value"},"max_value":{"title":"Max Value"},"updated_at":{"title":"Updated At"}},"type":"object","required":["key","value","value_type","category","description","min_value","max_value","updated_at"],"title":"NeuralConfigItem","description":"Neural config item response."},"NeuralConfigListResponse":{"properties":{"configs":{"items":{"$ref":"#/components/schemas/NeuralConfigItem"},"type":"array","title":"Configs"},"categories":{"items":{"type":"string"},"type":"array","title":"Categories"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["configs","categories","total"],"title":"NeuralConfigListResponse","description":"List of neural config items."},"NeuralConfigResetResponse":{"properties":{"message":{"type":"string","title":"Message"},"reset_count":{"type":"integer","title":"Reset Count"}},"type":"object","required":["message","reset_count"],"title":"NeuralConfigResetResponse","description":"Reset neural config response."},"NeuralConfigUpdateRequest":{"properties":{"value":{"type":"string","title":"Value"}},"type":"object","required":["value"],"title":"NeuralConfigUpdateRequest","description":"Update neural config request."},"NeuralConfigUpdateResponse":{"properties":{"key":{"type":"string","title":"Key"},"old_value":{"type":"string","title":"Old Value"},"new_value":{"type":"string","title":"New Value"},"message":{"type":"string","title":"Message"}},"type":"object","required":["key","old_value","new_value","message"],"title":"NeuralConfigUpdateResponse","description":"Update neural config response."},"OAuth2ClientCreateRequest":{"properties":{"client_name":{"type":"string","maxLength":100,"minLength":1,"title":"Client Name","description":"Human-readable client name"},"redirect_uris":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Redirect Uris","description":"Allowed redirect URIs"},"provider":{"type":"string","pattern":"^(claude|chatgpt|cursor|custom)$","title":"Provider","description":"OAuth provider type (Migration 036)","default":"custom"},"grant_types":{"items":{"type":"string"},"type":"array","title":"Grant Types","description":"Allowed grant types","default":["authorization_code","refresh_token"]},"response_types":{"items":{"type":"string"},"type":"array","title":"Response Types","description":"Allowed response types","default":["code"]},"scope":{"type":"string","title":"Scope","description":"Space-separated scopes","default":"openid memory:read memory:write memory:delete offline_access"},"token_endpoint_auth_method":{"type":"string","title":"Token Endpoint Auth Method","description":"Client authentication method","default":"client_secret_post"}},"type":"object","required":["client_name","redirect_uris"],"title":"OAuth2ClientCreateRequest","description":"OAuth2 Client creation request."},"OAuth2ClientResponse":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"grant_types":{"items":{"type":"string"},"type":"array","title":"Grant Types"},"response_types":{"items":{"type":"string"},"type":"array","title":"Response Types"},"scope":{"type":"string","title":"Scope"},"token_endpoint_auth_method":{"type":"string","title":"Token Endpoint Auth Method"},"owner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Id"},"provider":{"type":"string","title":"Provider"},"created_at":{"type":"string","title":"Created At"},"plaintext_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plaintext Secret"},"is_visible":{"type":"boolean","title":"Is Visible"},"visibility_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility Expires At"}},"type":"object","required":["client_id","client_name","redirect_uris","grant_types","response_types","scope","token_endpoint_auth_method","owner_id","provider","created_at","is_visible"],"title":"OAuth2ClientResponse","description":"OAuth2 Client response (without secret).\n\nIssue #1813: no integer ``id`` — clients are addressed by ``client_id``."},"OAuth2ClientUpdateRequest":{"properties":{"client_name":{"anyOf":[{"type":"string","maxLength":100,"minLength":1},{"type":"null"}],"title":"Client Name"},"redirect_uris":{"anyOf":[{"items":{"type":"string"},"type":"array","minItems":1},{"type":"null"}],"title":"Redirect Uris"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"},"token_endpoint_auth_method":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Endpoint Auth Method","description":"Client authentication method (none, client_secret_post, client_secret_basic)"}},"type":"object","title":"OAuth2ClientUpdateRequest","description":"OAuth2 Client update request."},"OAuth2ClientWithSecretResponse":{"properties":{"client_id":{"type":"string","title":"Client Id"},"client_name":{"type":"string","title":"Client Name"},"redirect_uris":{"items":{"type":"string"},"type":"array","title":"Redirect Uris"},"grant_types":{"items":{"type":"string"},"type":"array","title":"Grant Types"},"response_types":{"items":{"type":"string"},"type":"array","title":"Response Types"},"scope":{"type":"string","title":"Scope"},"token_endpoint_auth_method":{"type":"string","title":"Token Endpoint Auth Method"},"owner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Owner Id"},"provider":{"type":"string","title":"Provider"},"created_at":{"type":"string","title":"Created At"},"plaintext_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plaintext Secret"},"is_visible":{"type":"boolean","title":"Is Visible"},"visibility_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Visibility Expires At"},"client_secret":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Secret"}},"type":"object","required":["client_id","client_name","redirect_uris","grant_types","response_types","scope","token_endpoint_auth_method","owner_id","provider","created_at","is_visible"],"title":"OAuth2ClientWithSecretResponse","description":"OAuth2 Client response with client secret (only on creation).\n\n``client_secret`` is ``None`` for public clients\n(``token_endpoint_auth_method=\"none\"``) per RFC 7591 §3.2.1, which\nforbids issuing a secret to clients that won't authenticate at the\ntoken endpoint. Confidential clients always receive a value."},"OAuth2ProviderResponse":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"authorization_url":{"type":"string","title":"Authorization Url"},"token_url":{"type":"string","title":"Token Url"},"scopes":{"items":{"type":"string"},"type":"array","title":"Scopes"},"enabled":{"type":"boolean","title":"Enabled"},"configured":{"type":"boolean","title":"Configured"}},"type":"object","required":["name","display_name","client_id","authorization_url","token_url","scopes","enabled","configured"],"title":"OAuth2ProviderResponse","description":"OAuth2 Provider response."},"OAuthGrantDefaultContextUpdate":{"properties":{"default_context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Default Context Id","description":"The grant's MCP default context; null clears it"}},"type":"object","required":["default_context_id"],"title":"OAuthGrantDefaultContextUpdate","description":"Body of ``PUT /me/oauth/grants/{client_id}/default-context``."},"OAuthGrantResponse":{"properties":{"client_id":{"title":"Client Id","description":"The OAuth client's id"},"client_name":{"title":"Client Name","description":"The client's registered name"},"scopes":{"title":"Scopes","description":"Scopes of the caller's live tokens for this client"},"default_context_id":{"title":"Default Context Id","description":"The grant's MCP default context; null when none"},"default_context_name":{"title":"Default Context Name","description":"Name of the default context; null when none is set or the caller can no longer read it"},"last_issued_at":{"title":"Last Issued At","description":"When the newest live token was issued; null when none is live"}},"type":"object","required":["client_id","client_name"],"title":"OAuthGrantResponse","description":"One OAuth app the caller has authorized."},"OpenAIKeyStatusResponse":{"properties":{"has_key":{"type":"boolean","title":"Has Key","description":"Whether workspace has OpenAI API key"},"can_configure":{"type":"boolean","title":"Can Configure","description":"Whether current user can configure keys"},"external_keys_url":{"type":"string","title":"External Keys Url","description":"URL to external keys configuration page"},"embedding_available":{"type":"boolean","title":"Embedding Available","description":"Whether embeddings can actually be generated for this workspace — own key OR the platform credential. Use this for availability warnings; use has_key only to describe the workspace's own keys.","default":false}},"type":"object","required":["has_key","can_configure","external_keys_url"],"title":"OpenAIKeyStatusResponse","description":"OpenAI API key status for workspace.\n\nIssue #181: OpenAI API key guidance in context creation."},"PaginatedResourceTokensResponse":{"properties":{"tokens":{"items":{"$ref":"#/components/schemas/ResourceTokenResponse"},"type":"array","title":"Tokens","description":"List of resource tokens"},"total":{"type":"integer","title":"Total","description":"Total number of tokens matching filter"},"limit":{"type":"integer","title":"Limit","description":"Number of tokens per page"},"offset":{"type":"integer","title":"Offset","description":"Starting offset"}},"type":"object","required":["tokens","total","limit","offset"],"title":"PaginatedResourceTokensResponse","description":"Paginated response for resource tokens.\n\nIssue #264: Pagination support for large token lists."},"PasswordChangeBody":{"properties":{"current_password":{"type":"string","title":"Current Password"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["current_password","new_password"],"title":"PasswordChangeBody","description":"Body for POST /me/password/change."},"PasswordEmailAcceptedResponse":{"properties":{"status":{"type":"string","const":"accepted","title":"Status","default":"accepted"},"message":{"type":"string","title":"Message","default":"If an account with that email exists, we sent a link to reset its password."}},"type":"object","title":"PasswordEmailAcceptedResponse","description":"Identical for every reset request, whether or not an email was sent."},"PasswordLinkBody":{"properties":{"token":{"type":"string","maxLength":256,"minLength":1,"title":"Token"},"new_password":{"type":"string","title":"New Password"}},"type":"object","required":["token","new_password"],"title":"PasswordLinkBody","description":"Body for POST /auth/password/reset and /auth/password/setup.\n\nNo ``max_length`` on ``new_password``: the policy check answers an\nover-long password with its own message (bcrypt's 72-byte limit)."},"PasswordLoginRequest":{"properties":{"login_id":{"type":"string","title":"Login Id"},"password":{"type":"string","title":"Password"},"accepted_terms":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Accepted Terms"}},"type":"object","required":["login_id","password"],"title":"PasswordLoginRequest","description":"Password login request."},"PasswordRemoveBody":{"properties":{"current_password":{"type":"string","title":"Current Password"}},"type":"object","required":["current_password"],"title":"PasswordRemoveBody","description":"Body for DELETE /me/password."},"PasswordResetRequestBody":{"properties":{"email":{"type":"string","maxLength":320,"minLength":3,"title":"Email"}},"type":"object","required":["email"],"title":"PasswordResetRequestBody","description":"Body for POST /auth/password/reset-request."},"PasswordSetupRequestedResponse":{"properties":{"status":{"type":"string","const":"sent","title":"Status","default":"sent"}},"type":"object","title":"PasswordSetupRequestedResponse","description":"Returned once the set-up link was emailed to the signed-in user."},"PatchMemoryRequest":{"properties":{"summary":{"anyOf":[{"type":"string","maxLength":500,"minLength":10},{"type":"null"}],"title":"Summary"},"content":{"anyOf":[{"type":"string","minLength":1},{"type":"null"}],"title":"Content"},"type":{"anyOf":[{"type":"string","maxLength":50,"minLength":1},{"type":"null"}],"title":"Type"},"importance":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Importance"},"tags":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":100},{"type":"null"}],"title":"Tags"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"},"expected_version":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Expected Version"}},"type":"object","title":"PatchMemoryRequest","description":"Request schema for ``PATCH /api/v1/memory/{memory_id}`` (Issue #439).\n\nUUID-addressed partial update. ``memory_id`` is the URL path param, not a\nbody field — there is intentionally no ``external_id`` upsert mode here\n(use the existing ``update_memory`` MCP tool for that).\n\nAll fields are optional. Only fields explicitly provided are updated;\nomitted fields preserve their current value. ``tags`` follows replace-all\nsemantics (an empty list clears tags; a non-empty list replaces the\nwhole list). ``scope`` and ``context_id`` are intentionally excluded —\nthey have orthogonal lifecycles handled by separate operations."},"PendingInvitationItem":{"properties":{"id":{"title":"Id"},"workspace_id":{"title":"Workspace Id"},"workspace_name":{"title":"Workspace Name"},"role":{"title":"Role"},"invited_by":{"title":"Invited By"},"expires_at":{"title":"Expires At"},"created_at":{"title":"Created At"},"token":{"title":"Token"},"invitation_url":{"title":"Invitation Url"}},"type":"object","required":["id","workspace_id","workspace_name","role","invited_by","expires_at","created_at","token","invitation_url"],"title":"PendingInvitationItem","description":"Pending invitation item for current user.\n\nIssue #179: In-app invitation notifications."},"PendingInvitationsResponse":{"properties":{"pending_invitations":{"items":{"$ref":"#/components/schemas/PendingInvitationItem"},"type":"array","title":"Pending Invitations"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["pending_invitations","count"],"title":"PendingInvitationsResponse","description":"Response for pending invitations query.\n\nIssue #179: In-app invitation notifications."},"PersistenceInfo":{"properties":{"scope":{"type":"string","enum":["working","persistent"],"title":"Scope"},"committed":{"type":"boolean","title":"Committed","description":"The memory row is committed to PostgreSQL before this response is returned. Always true — scope does not gate durability.","default":true},"promotes_via":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promotes Via","description":"The maintenance pass that can promote this memory to scope='persistent' on this deployment: 'sleep_consolidation', 'legacy_consolidation', or null when the memory is already persistent or no consolidation pass is enabled here."},"consolidation_archive_min_age_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Consolidation Archive Min Age Days","description":"Age floor CONSOLIDATION applies before it may archive this memory; it also requires zero adoption. Scoped to consolidation only — it is not a retention guarantee, and does not bind near-duplicate merge (which skips pinned memories) or an explicit forget(). Null when no consolidation pass is enabled, or when the memory is persistent (consolidation acts only on scope='working')."},"detail":{"type":"string","title":"Detail","description":"One-line summary of the above for the calling agent."}},"type":"object","required":["scope","detail"],"title":"PersistenceInfo","description":"What a write's ``scope`` means for durability.\n\n``scope=\"working\"`` reads as \"not saved yet\", which is wrong: the row is\ncommitted before the write returns. This block says what the scope actually\nimplies, so a caller does not have to guess whether a memory survives until\nconsolidation promotes it.\n\nThis is NOT a retention SLA. It describes the consolidation lifecycle only;\nother maintenance (notably near-duplicate merge) has its own rules — pinned\nmemories (``delivery_mode='always'``) are exempt from it — and an\nexplicit forget() removes a memory at any time."},"PinnedMemoryItem":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"context_summary":{"title":"Context Summary"},"type":{"title":"Type"},"importance":{"title":"Importance"},"delivery_mode":{"title":"Delivery Mode"},"created_at":{"title":"Created At"}},"type":"object","required":["memory_id","summary","context_summary","type","importance","delivery_mode","created_at"],"title":"PinnedMemoryItem","description":"A single always-load memory (Issue #886).\n\nDeliberately L1 + L2 only (summary + context_summary) — the deterministic\nalways-load path injects these into the agent's context every turn, so the\nfull L3 ``content`` is intentionally omitted and fetched on demand via\n``reference(memory_id)``. No ``score`` field: this path is unranked."},"PlanChangeAuditEntry":{"properties":{"id":{"type":"integer","title":"Id"},"workspace_id":{"type":"string","title":"Workspace Id"},"workspace_name":{"type":"string","title":"Workspace Name"},"old_plan":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Plan"},"new_plan":{"type":"string","title":"New Plan"},"changed_by":{"type":"string","title":"Changed By"},"changed_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changed By Name"},"changed_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changed By Email"},"changed_at":{"type":"string","title":"Changed At"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["id","workspace_id","workspace_name","old_plan","new_plan","changed_by","changed_at","reason"],"title":"PlanChangeAuditEntry","description":"Plan change audit log entry."},"PlanLimits":{"properties":{"plan_name":{"type":"string","title":"Plan Name","description":"Plan tier key: free | basic | pro | promax"},"memory_limit":{"type":"integer","title":"Memory Limit","description":"Maximum memories allowed"},"daily_total_limit":{"type":"integer","title":"Daily Total Limit","description":"Combined daily limit (MCP + REST + Public)"},"weekly_total_limit":{"type":"integer","title":"Weekly Total Limit","description":"Combined weekly limit (MCP + REST + Public)"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day","description":"MCP API daily limit","default":0},"mcp_calls_per_week":{"type":"integer","title":"Mcp Calls Per Week","description":"MCP API weekly limit","default":0},"rest_calls_per_day":{"type":"integer","title":"Rest Calls Per Day","description":"REST API daily limit","default":0},"rest_calls_per_week":{"type":"integer","title":"Rest Calls Per Week","description":"REST API weekly limit","default":0},"public_calls_per_day":{"type":"integer","title":"Public Calls Per Day","description":"Public REST API daily limit","default":0},"public_calls_per_week":{"type":"integer","title":"Public Calls Per Week","description":"Public REST API weekly limit","default":0},"memories_per_day":{"type":"integer","title":"Memories Per Day","description":"Memories that may be created per UTC day (Issue #1549)","default":0}},"type":"object","required":["plan_name","memory_limit","daily_total_limit","weekly_total_limit"],"title":"PlanLimits","description":"Plan limits and quotas.\n\nIssue #198: ``daily_api_limit`` / ``weekly_api_limit`` are the\nsums of every API tier (MCP + REST + Public) so the dashboard's\n\"API Calls Today\" card stays meaningful when looking at the\naggregate. Use the per-tier fields below for accurate breakdowns\nthat match ``plan_tiers.py``. Public callers and the rate limiter\nshould always read the per-tier fields, never the legacy combined\nsums."},"PlanTierFeature":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"max_contexts":{"type":"integer","title":"Max Contexts"},"max_members":{"type":"integer","title":"Max Members"},"owned_workspaces":{"type":"integer","title":"Owned Workspaces"},"memory_limit":{"type":"integer","title":"Memory Limit"},"memories_per_day":{"type":"integer","title":"Memories Per Day"},"storage_limit_bytes":{"type":"integer","title":"Storage Limit Bytes"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day"},"rest_calls_per_day":{"type":"integer","title":"Rest Calls Per Day"},"public_calls_per_day":{"type":"integer","title":"Public Calls Per Day"},"max_resource_tokens":{"type":"integer","title":"Max Resource Tokens"},"max_connectors":{"type":"integer","title":"Max Connectors"},"max_content_chunk_contexts":{"type":"integer","title":"Max Content Chunk Contexts"},"max_llm_selector_contexts":{"type":"integer","title":"Max Llm Selector Contexts"},"analysis_runs_per_day":{"type":"integer","title":"Analysis Runs Per Day"},"sleep_enabled_contexts_limit":{"type":"integer","title":"Sleep Enabled Contexts Limit"},"reranking":{"type":"boolean","title":"Reranking"},"managed_embeddings":{"type":"boolean","title":"Managed Embeddings"},"managed_llm":{"type":"boolean","title":"Managed Llm"},"secret_store":{"type":"boolean","title":"Secret Store"},"shared_contexts":{"type":"boolean","title":"Shared Contexts"},"team_invitations":{"type":"boolean","title":"Team Invitations"},"resources":{"type":"boolean","title":"Resources"},"connectors":{"type":"boolean","title":"Connectors"},"public_contexts":{"type":"boolean","title":"Public Contexts"},"content_chunks":{"type":"boolean","title":"Content Chunks"},"llm_selector":{"type":"boolean","title":"Llm Selector"},"feature_enforcement":{"additionalProperties":{"type":"string"},"type":"object","title":"Feature Enforcement"}},"type":"object","required":["name","display_name","max_contexts","max_members","owned_workspaces","memory_limit","memories_per_day","storage_limit_bytes","mcp_calls_per_day","rest_calls_per_day","public_calls_per_day","max_resource_tokens","max_connectors","max_content_chunk_contexts","max_llm_selector_contexts","analysis_runs_per_day","sleep_enabled_contexts_limit","reranking","managed_embeddings","managed_llm","secret_store","shared_contexts","team_invitations","resources","connectors","public_contexts","content_chunks","llm_selector","feature_enforcement"],"title":"PlanTierFeature","description":"One tier's curated feature/limit values for the comparison matrix (#1138).\n\nSingle source of truth = ``config/plan_tiers.py`` (env-overridable). Price is\nintentionally omitted — pricing lives in www / the payment service; this\nsurface is feature *limits* only, so the OSS Plan page never fabricates an\namount (consistent with #1141 / #1096). Numeric fields use ``0`` for \"not\navailable on this tier\"; the frontend renders that as ✗."},"PlanTierInfo":{"properties":{"name":{"type":"string","title":"Name"},"display_name":{"type":"string","title":"Display Name"},"max_contexts_per_workspace":{"type":"integer","title":"Max Contexts Per Workspace"},"max_members_per_workspace":{"type":"integer","title":"Max Members Per Workspace"},"owned_workspace_grant":{"type":"integer","title":"Owned Workspace Grant"},"owned_workspaces":{"type":"integer","title":"Owned Workspaces"},"max_resource_tokens":{"type":"integer","title":"Max Resource Tokens"},"max_connectors":{"type":"integer","title":"Max Connectors"},"max_content_chunk_contexts":{"type":"integer","title":"Max Content Chunk Contexts"},"max_llm_selector_contexts":{"type":"integer","title":"Max Llm Selector Contexts"},"memory_limit":{"type":"integer","title":"Memory Limit"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day"},"mcp_calls_per_week":{"type":"integer","title":"Mcp Calls Per Week"},"rest_calls_per_day":{"type":"integer","title":"Rest Calls Per Day"},"rest_calls_per_week":{"type":"integer","title":"Rest Calls Per Week"},"public_calls_per_day":{"type":"integer","title":"Public Calls Per Day"},"public_calls_per_week":{"type":"integer","title":"Public Calls Per Week"},"bound_public_calls_per_minute":{"type":"integer","title":"Bound Public Calls Per Minute"},"analysis_runs_per_day":{"type":"integer","title":"Analysis Runs Per Day"},"storage_limit_bytes":{"type":"integer","title":"Storage Limit Bytes"},"sleep_enabled_contexts_limit":{"type":"integer","title":"Sleep Enabled Contexts Limit"},"memories_per_day":{"type":"integer","title":"Memories Per Day"},"embedding_daily_cap_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Embedding Daily Cap Usd"},"embedding_monthly_cap_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Embedding Monthly Cap Usd"},"allows_shared_contexts":{"type":"boolean","title":"Allows Shared Contexts"},"resources":{"type":"boolean","title":"Resources"},"connectors":{"type":"boolean","title":"Connectors"},"public_contexts":{"type":"boolean","title":"Public Contexts"},"features":{"items":{"type":"string"},"type":"array","title":"Features"}},"type":"object","required":["name","display_name","max_contexts_per_workspace","max_members_per_workspace","owned_workspace_grant","owned_workspaces","max_resource_tokens","max_connectors","max_content_chunk_contexts","max_llm_selector_contexts","memory_limit","mcp_calls_per_day","mcp_calls_per_week","rest_calls_per_day","rest_calls_per_week","public_calls_per_day","public_calls_per_week","bound_public_calls_per_minute","analysis_runs_per_day","storage_limit_bytes","sleep_enabled_contexts_limit","memories_per_day","allows_shared_contexts","resources","connectors","public_contexts","features"],"title":"PlanTierInfo","description":"Plan tier configuration served to the admin tiers comparison table.\n\nIssue #664. Reflects environment variable overrides applied at module\nimport time (see ``config.plan_tiers._apply_settings_overrides``)."},"PositionListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PositionRow"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"PositionListResponse","description":"All scatter positions for a run, ordered by ``cluster_index``."},"PositionRow":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"x":{"type":"number","title":"X"},"y":{"type":"number","title":"Y"},"cluster_index":{"type":"integer","title":"Cluster Index"}},"type":"object","required":["memory_id","x","y","cluster_index"],"title":"PositionRow","description":"One ``(memory_id, x, y, cluster_index)`` row for scatter rendering."},"PrivateContextAggregation":{"properties":{"context_count":{"type":"integer","title":"Context Count"},"memory_count":{"type":"integer","title":"Memory Count"}},"type":"object","required":["context_count","memory_count"],"title":"PrivateContextAggregation","description":"Aggregated statistics for inaccessible private contexts.\n\nSingle Collection Migration: storage_mb removed (memory count only)."},"PromoteToSystemAdminRequest":{"properties":{"user_id":{"type":"string","title":"User Id","description":"OAuth2 user_id to promote"}},"type":"object","required":["user_id"],"title":"PromoteToSystemAdminRequest","description":"Request to promote user to system admin.\n\nIssue #166: System Admin promotion."},"PromoteToSystemAdminResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"user":{"$ref":"#/components/schemas/UserWithAdminFlag"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","user","message"],"title":"PromoteToSystemAdminResponse","description":"Response for system admin promotion.\n\nIssue #166: System Admin promotion result."},"ProviderInfo":{"properties":{"name":{"type":"string","title":"Name"}},"type":"object","required":["name"],"title":"ProviderInfo","description":"OAuth provider info."},"ProvidersListResponse":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/LinkedProvider"},"type":"array","title":"Providers"}},"type":"object","required":["providers"],"title":"ProvidersListResponse","description":"All OAuth providers currently linked to the session user."},"ProvidersResponse":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/ProviderInfo"},"type":"array","title":"Providers"}},"type":"object","required":["providers"],"title":"ProvidersResponse","description":"Available OAuth providers."},"PubkeyRegister":{"properties":{"pubkey":{"type":"string","maxLength":128,"minLength":8,"title":"Pubkey","description":"age recipient public key (age1…)"},"label":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Label","description":"Friendly label"}},"type":"object","required":["pubkey"],"title":"PubkeyRegister","description":"Register the caller's own age recipient pubkey (becomes pending)."},"PubkeyResponse":{"properties":{"id":{"title":"Id"},"identity_id":{"title":"Identity Id"},"pubkey":{"title":"Pubkey"},"fingerprint":{"title":"Fingerprint"},"label":{"title":"Label"},"status":{"title":"Status"},"created_at":{"title":"Created At"},"attested_at":{"title":"Attested At"},"revoked_at":{"title":"Revoked At"}},"type":"object","required":["id","identity_id","pubkey","fingerprint","label","status","created_at","attested_at","revoked_at"],"title":"PubkeyResponse","description":"Recipient pubkey metadata (the pubkey is public; no private material)."},"PublicAPIStatsResponse":{"properties":{"resource_ingest":{"$ref":"#/components/schemas/ResourceIngestStats"},"public_search":{"$ref":"#/components/schemas/PublicSearchStats"}},"type":"object","required":["resource_ingest","public_search"],"title":"PublicAPIStatsResponse","description":"Response model for public API statistics.\n\nIssue #265: GET /api/v1/workspaces/{workspace_id}/contexts/{context_id}/public-api-stats"},"PublicSearchRequest":{"properties":{"query":{"type":"string","maxLength":500,"minLength":1,"title":"Query","description":"Search query"},"limit":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"Limit","description":"Maximum results (default: 10, max: 100)","default":10},"use_rerank":{"type":"boolean","title":"Use Rerank","description":"Enable reranking for better results","default":false},"search_mode":{"type":"string","pattern":"^(hybrid|semantic|keyword)$","title":"Search Mode","description":"Search mode: hybrid (default), semantic, keyword","default":"hybrid"}},"type":"object","required":["query"],"title":"PublicSearchRequest","description":"Public search request."},"PublicSearchResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"query":{"type":"string","title":"Query"},"results":{"items":{"$ref":"#/components/schemas/PublicSearchResult"},"type":"array","title":"Results"},"count":{"type":"integer","title":"Count"},"context_id":{"type":"string","title":"Context Id"},"resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Id"},"schema_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Schema Version"},"degraded":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Degraded"},"degraded_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Degraded Reason"}},"type":"object","required":["query","results","count","context_id"],"title":"PublicSearchResponse","description":"Public search response."},"PublicSearchResult":{"properties":{"memory_id":{"type":"string","title":"Memory Id"},"content":{"type":"string","title":"Content"},"score":{"type":"number","title":"Score"},"metadata":{"additionalProperties":true,"type":"object","title":"Metadata"},"highlighted":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Highlighted"}},"type":"object","required":["memory_id","content","score","metadata"],"title":"PublicSearchResult","description":"Single search result with schema-aware formatting."},"PublicSearchStats":{"properties":{"total_searches":{"type":"integer","title":"Total Searches"},"last_n_days":{"type":"integer","title":"Last N Days"},"anonymous":{"type":"integer","title":"Anonymous"},"authenticated":{"type":"integer","title":"Authenticated"},"timeline":{"items":{"$ref":"#/components/schemas/SearchTimelineItem"},"type":"array","title":"Timeline"}},"type":"object","required":["total_searches","last_n_days","anonymous","authenticated","timeline"],"title":"PublicSearchStats","description":"Public Search API statistics.\n\nIssue #265: Public API usage stats."},"QuotaBreakdown":{"properties":{"memory_limit":{"type":"integer","title":"Memory Limit"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day"},"max_contexts":{"type":"integer","title":"Max Contexts"},"max_members":{"type":"integer","title":"Max Members"},"analysis_runs_per_day":{"type":"integer","title":"Analysis Runs Per Day"},"rest_calls_per_day":{"type":"integer","title":"Rest Calls Per Day","default":0},"public_calls_per_day":{"type":"integer","title":"Public Calls Per Day","default":0},"storage_bytes_limit":{"type":"integer","title":"Storage Bytes Limit","default":0},"sleep_enabled_contexts_limit":{"type":"integer","title":"Sleep Enabled Contexts Limit","default":0},"max_resource_tokens":{"type":"integer","title":"Max Resource Tokens","default":0},"max_connectors":{"type":"integer","title":"Max Connectors","default":0},"memories_per_day":{"type":"integer","title":"Memories Per Day","default":0},"max_content_chunk_contexts":{"type":"integer","title":"Max Content Chunk Contexts","default":0},"max_llm_selector_contexts":{"type":"integer","title":"Max Llm Selector Contexts","default":0}},"type":"object","required":["memory_limit","mcp_calls_per_day","max_contexts","max_members","analysis_runs_per_day"],"title":"QuotaBreakdown","description":"Quota values for a single tier (Issue #665).\n\nExtended to 9 fields so the GET ``effective`` block surfaces every\naddon type the PUT handler accepts — review-finding #8 fixed the\nwrite-only hole where storage / rest_quota / public_quota /\nsleep_contexts could be set but not read back as an effective value.\n\nThe legacy 5 fields stay required (callers always populated them);\nthe 4 new fields default to 0 so existing TS consumers ignore the\nadditive keys without runtime impact."},"RecalibrateResponse":{"properties":{"accepted":{"type":"boolean","title":"Accepted"},"model_name":{"type":"string","title":"Model Name"},"dimensions":{"type":"integer","title":"Dimensions"}},"type":"object","required":["accepted","model_name","dimensions"],"title":"RecalibrateResponse","description":"202 response body for the admin recalibrate endpoint.\n\nAttributes:\n    accepted: ``True`` when the dedup lock was acquired and a\n        calibration task was spawned. ``False`` when a prior job is\n        still in-flight for the same ``(model, dimensions)`` —\n        idempotent; no error raised.\n    model_name: Echo of the requested model.\n    dimensions: Echo of the requested dimensionality."},"RecallConfidence":{"properties":{"level":{"type":"string","enum":["high","moderate","low","none"],"title":"Level"},"top_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Top Score","description":"Raw semantic cosine of the best hit (absolute match strength, 0-1). On keyword-only searches this is the normalized hybrid top instead."},"prominence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Prominence","description":"(top_score - mean_background_cosine) / mean_background_cosine — a model-scale-invariant match prominence and the primary basis for level. None when <2 candidates or keyword-only."},"relative_margin":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Relative Margin","description":"Separation of the top hit from the candidate-pool background, in background std-devs. Informational only — it inflates on flat off-topic tails and is not used for level. None when <2 candidates."},"result_count":{"type":"integer","title":"Result Count"},"rationale":{"type":"string","title":"Rationale"}},"type":"object","required":["level","result_count","rationale"],"title":"RecallConfidence","description":"Issue #1047/#1052: a signal distinguishing \"relevant results found\" from\n\"likely nothing relevant\" (so an agent can stop probing / go external instead\nof trusting a weak hit), WITHOUT changing ranking.\n\n``level`` is driven by **absolute semantic match strength**, made robust to\nembedding-model scale via ``prominence`` (#1052):\n\n- ``top_score`` — the RAW semantic cosine of the best hit (absolute match\n  strength in [0, 1]). High ⇒ something closely matches the query.\n- ``prominence`` — ``(top_score - mean_background_cosine) /\n  mean_background_cosine`` over the candidate pool. A *ratio*, so it is\n  invariant to the overall cosine scale a given embedding model produces\n  (text-embedding-3-small and qwen3-embedding sit at different absolute\n  cosines, but a real match is proportionally far above its background in\n  both). ``level`` thresholds on this; a near-duplicate ``top_score`` floors\n  ``level`` to at least ``moderate``.\n\nHow an agent should use it (it is a TRIAGE hint, not a correctness verdict):\n\n- ``none`` / ``low`` → likely nothing relevant; prefer an external source over\n  forcing an answer out of these results. This is the high-value signal and is\n  reliable without a reranker.\n- ``high`` / ``moderate`` → relevant memory is likely present; READ the returned\n  summaries and judge from their content. ``level`` measures *topical match\n  strength*, so a closely-related \"near-miss\" (an adjacent topic) can also read\n  ``high`` — it does NOT guarantee the exact fact you asked for is stored. The\n  returned content is the source of truth; ``level`` only says whether it is\n  worth reading. Distinguishing near-miss from an exact match needs a\n  cross-encoder (pass ``use_rerank=true``); plain bi-encoder cosine cannot.\n\nWhy this supersedes the original #1047 z-score approach: that bucketed on\n``relative_margin`` (background std-devs), but max-normalization in the hybrid\nmerge erases absolute strength, and dividing by a flat off-topic tail's tiny\nstd *inflated* the margin — so irrelevant queries scored ``high`` (benchmarked).\n``relative_margin`` is retained for transparency but is NO LONGER the basis for\n``level``; note it reads high even for off-topic queries.\n\n``prominence`` / ``relative_margin`` are None for keyword-only searches (no\nsemantic cosines) and single-candidate pools; there ``level`` falls back to the\noriginal z-score separation heuristic."},"RecallRequest":{"properties":{"query":{"type":"string","maxLength":8000,"minLength":1,"title":"Query","description":"検索クエリ"},"k":{"type":"integer","maximum":100.0,"minimum":1.0,"title":"K","description":"返却結果数","default":5},"use_rerank":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Use Rerank","description":"Reranking (Voyage/Cohere BYOK, or self_hosted). Omit to follow the context's search config; set false to force off."},"filters":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Filters","description":"オプショナルフィルタ"},"search_mode":{"anyOf":[{"type":"string","pattern":"^(hybrid|semantic|keyword)$"},{"type":"null"}],"title":"Search Mode","description":"Search mode: hybrid (default when omitted), semantic (vector only), keyword (BM25 only). Omit to allow query-intent routing on contexts with routing_mode='active' (#1212)."},"include_explore_hints":{"type":"boolean","title":"Include Explore Hints","description":"Include up to 3 explore_hints in response suggesting good seeds for explore()","default":false},"include_superseded":{"type":"boolean","title":"Include Superseded","description":"Include memories shadowed by a supersedes edge (annotated with superseded_by) and return folded older versions as their own rows","default":false},"full":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Full","description":"How many of the top results carry content, each cut at 1,500 characters (content_truncated / content_next_offset when cut). Omit or 0 for summaries only."}},"type":"object","required":["query"],"title":"RecallRequest","description":"Request schema for recall() API (Hybrid Search).\n\nExample:\n    {\n        \"query\": \"認証エラーの解決方法\",\n        \"k\": 5,\n        \"use_rerank\": false,\n        \"filters\": {\n            \"context_id\": \"my-context\",\n            \"scope\": \"persistent\",\n            \"type\": \"code\"\n        }\n    }"},"RecallResponse":{"properties":{"results":{"items":{"$ref":"#/components/schemas/MemoryResponse"},"type":"array","title":"Results"},"related_tags":{"items":{"$ref":"#/components/schemas/RelatedTagItem"},"type":"array","title":"Related Tags","default":[]},"explore_hints":{"anyOf":[{"items":{"$ref":"#/components/schemas/ExploreHint"},"type":"array"},{"type":"null"}],"title":"Explore Hints"},"tag_suggestions":{"anyOf":[{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"},{"type":"null"}],"title":"Tag Suggestions","description":"Tags present in this context that resemble the ones filtered on, keyed by the requested tag. Present only when a tag filter matched no memories. Advisory: the filter itself was not widened."},"confidence":{"anyOf":[{"$ref":"#/components/schemas/RecallConfidence"},{"type":"null"}]},"degraded":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Degraded","description":"True when this result set was produced without the semantic search arm. Absent when the search ran normally."},"degraded_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Degraded Reason","description":"Why the search degraded: 'embedding_unavailable' (the embedding provider failed) or 'vector_search_unavailable' (the vector store failed). Absent when the search ran normally."},"selector":{"anyOf":[{"$ref":"#/components/schemas/SelectorReport"},{"type":"null"}]}},"type":"object","required":["results"],"title":"RecallResponse","description":"Response schema for recall() API.\n\nIssue #104: Added related_tags to help LLMs understand tag context.\nIssue #216: Added explore_hints for graph discovery bridging.\nIssue #1047: Added confidence (relevance/staleness signal) — additive."},"RedeemRequest":{"properties":{"code":{"type":"string","maxLength":24,"minLength":1,"title":"Code"}},"type":"object","required":["code"],"title":"RedeemRequest","description":"A referral code submitted by a newly-signed-up user."},"ReferenceRequest":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"version":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Version"}},"type":"object","required":["memory_id"],"title":"ReferenceRequest","description":"Request schema for reference() API."},"ReferenceResponse":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"context_summary":{"title":"Context Summary"},"content":{"title":"Content"},"details":{"title":"Details"},"type":{"title":"Type"},"scope":{"title":"Scope"},"importance":{"title":"Importance"},"tags":{"title":"Tags"},"context":{"title":"Context"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"},"version":{"title":"Version"},"historical":{"title":"Historical"},"current_version":{"title":"Current Version"},"unavailable_fields":{"title":"Unavailable Fields"},"client":{"title":"Client"},"source_uri":{"title":"Source Uri"},"source_type":{"title":"Source Type"},"outgoing_links":{"title":"Outgoing Links"},"outgoing_has_more":{"title":"Outgoing Has More"},"incoming_links":{"title":"Incoming Links"},"incoming_has_more":{"title":"Incoming Has More"},"supersede_candidate":{}},"type":"object","required":["memory_id","summary","context_summary","content","details","type","scope","importance","tags","context","created_at","updated_at","version","client"],"title":"ReferenceResponse","description":"Response schema for reference() API (full details)."},"ReferralGrantResponse":{"properties":{"id":{"title":"Id"},"granted_at":{"title":"Granted At"},"referrer_bonus_memories":{"title":"Referrer Bonus Memories"},"referred_bonus_memories":{"title":"Referred Bonus Memories"},"revoked_at":{"title":"Revoked At"}},"type":"object","required":["id","granted_at","referrer_bonus_memories","referred_bonus_memories","revoked_at"],"title":"ReferralGrantResponse","description":"A ledger row as seen by the user who earned it.\n\nInherits ``TZAwareBaseModel`` (not ``BaseModel``) because it carries\ndatetimes: the naive-UTC columns must serialize with a ``Z`` suffix or JS\nclients read them as local time."},"ReferralSummaryResponse":{"properties":{"code":{"type":"string","title":"Code"},"max_grants":{"type":"integer","title":"Max Grants"},"used_grants":{"type":"integer","title":"Used Grants"},"remaining_grants":{"type":"integer","title":"Remaining Grants"},"referee_reward_memories":{"type":"integer","title":"Referee Reward Memories"},"referrer_reward_memories":{"type":"integer","title":"Referrer Reward Memories"},"earned_memories":{"type":"integer","title":"Earned Memories"}},"type":"object","required":["code","max_grants","used_grants","remaining_grants","referee_reward_memories","referrer_reward_memories","earned_memories"],"title":"ReferralSummaryResponse","description":"The inviter's own standing.\n\nReward amounts are included because the user is being asked to share a link\n— they need to know what they and their invitee get. This is not the same as\nexposing them publicly: the route requires a session."},"RefreshOAuthRequest":{"properties":{"return_to":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Return To"}},"type":"object","title":"RefreshOAuthRequest","description":"Optional ``return_to`` lets the frontend control the post-callback\nlanding page. Must be a same-origin relative path starting with ``/``;\nabsolute or protocol-relative URLs are rejected at the endpoint to\nblock open-redirect after successful re-auth. Defaults to\n``/profile?refreshed=1`` so a vanilla button click works without\nextra wiring."},"RefreshOAuthResponse":{"properties":{"authorization_url":{"type":"string","title":"Authorization Url"},"state":{"type":"string","title":"State"}},"type":"object","required":["authorization_url","state"],"title":"RefreshOAuthResponse","description":"Frontend redirects ``window.location`` to ``authorization_url``."},"RegenerateAPIKeyResponse":{"properties":{"key":{"type":"string","title":"Key"},"key_prefix":{"type":"string","title":"Key Prefix"},"key_id":{"type":"string","pattern":"^akey_[0-9A-Za-z]{22}$","title":"Key Id"}},"type":"object","required":["key","key_prefix","key_id"],"title":"RegenerateAPIKeyResponse","description":"Response for API key regeneration.\n\nMigration 034: Returns new plaintext key (shown once)."},"RegenerateOAuthSecretResponse":{"properties":{"client_secret":{"type":"string","title":"Client Secret"},"client_id":{"type":"string","title":"Client Id"}},"type":"object","required":["client_secret","client_id"],"title":"RegenerateOAuthSecretResponse","description":"Response for OAuth client secret regeneration.\n\nMigration 034: Returns new plaintext secret (shown once)."},"RelatedMemoryResponse":{"properties":{"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"summary":{"type":"string","title":"Summary"},"context_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Summary"},"type":{"type":"string","title":"Type"},"activation":{"type":"number","title":"Activation","description":"活性化強度 (0.0-1.0)"},"hop":{"type":"integer","title":"Hop","description":"起点からのホップ数"},"weight":{"type":"number","title":"Weight","description":"エッジ重み"},"path":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Path","description":"起点からのパス"}},"type":"object","required":["memory_id","summary","context_summary","type","activation","hop","weight","path"],"title":"RelatedMemoryResponse","description":"Response schema for related memory in explore() API."},"RelatedTagItem":{"properties":{"tag":{"title":"Tag"},"count":{"title":"Count"},"sample_summary":{"title":"Sample Summary"},"last_used_at":{"title":"Last Used At"}},"type":"object","required":["tag","count"],"title":"RelatedTagItem","description":"Related tag with count, sample summary, and last-used timestamp.\n\nUsed by ``recall.related_tags`` (Issue #104 — populates ``sample_summary``)\nand ``list_tags`` (Issue #614 — populates ``last_used_at``). The shared\nschema means clients can unify their \"tag info\" type across both surfaces.\nInherits ``TZAwareBaseModel`` so the optional ``last_used_at`` serializes\nwith an explicit UTC ``Z`` suffix when populated."},"RememberRequest":{"properties":{"summary":{"type":"string","maxLength":500,"minLength":10,"title":"Summary","description":"検索用サマリー"},"context_summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Context Summary","description":"背景・文脈説明"},"content":{"type":"string","minLength":1,"title":"Content","description":"基本内容"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details","description":"完全詳細（JSONB）"},"type":{"type":"string","maxLength":50,"minLength":1,"title":"Type","description":"メモリタイプ"},"importance":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Importance","description":"重要度 (0.0-1.0)","default":0.5},"tags":{"items":{"type":"string"},"type":"array","title":"Tags","description":"タグ"},"context":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Context","description":"コンテキスト情報"},"delivery_mode":{"type":"string","enum":["always","on_recall","on_trigger"],"title":"Delivery Mode","description":"Delivery mode: always (pin, load every turn) | on_recall (default) | on_trigger","default":"on_recall"},"source_uri":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Source Uri","description":"Origin URI: file://, http(s)://, vault://, obsidian://"},"source_type":{"anyOf":[{"type":"string","enum":["file","url","vault","api","manual"]},{"type":"null"}],"title":"Source Type","description":"Origin type"},"linked_memory_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Linked Memory Ids","description":"Explicit links to existing memories (creates declared_link edges)"},"linked_source_uris":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Linked Source Uris","description":"Explicit links by source_uri (resolved at remember time, unresolved silently skipped)"},"supersedes":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Supersedes","description":"Memory ID this new memory supersedes (old one is shadowed, not deleted)"}},"type":"object","required":["summary","content","type"],"title":"RememberRequest","description":"Request schema for remember() API.\n\nExample:\n    {\n        \"summary\": \"認証エラー修正。JWTトークン有効期限チェック追加。\",\n        \"context_summary\": \"ユーザーからログイン失敗の報告があり...\",\n        \"content\": \"auth.pyのverify_token関数にexpired_atの検証を追加\",\n        \"details\": {\"code_diff\": \"...\", \"test_results\": \"...\"},\n        \"type\": \"code\",\n        \"importance\": 0.8,\n        \"tags\": [\"python\", \"authentication\"],\n        \"context\": {\"context_id\": \"my-context\", \"file_path\": \"auth.py\"}\n    }"},"RememberResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"memory_id":{"type":"string","format":"uuid","title":"Memory Id"},"scope":{"type":"string","title":"Scope"},"version":{"type":"integer","title":"Version","default":1},"persistence":{"anyOf":[{"$ref":"#/components/schemas/PersistenceInfo"},{"type":"null"}]},"lint":{"items":{"$ref":"#/components/schemas/WriteLintHint"},"type":"array","title":"Lint"}},"type":"object","required":["memory_id","scope"],"title":"RememberResponse","description":"Response schema for remember() API."},"ResolvedTermEntry":{"properties":{"index":{"type":"integer","title":"Index"},"df_memory":{"type":"number","title":"Df Memory"},"df_global":{"type":"number","title":"Df Global"},"idf_memory":{"type":"number","title":"Idf Memory"},"idf_global":{"type":"number","title":"Idf Global"},"delta":{"type":"number","title":"Delta"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token"}},"type":"object","required":["index","df_memory","df_global","idf_memory","idf_global","delta"],"title":"ResolvedTermEntry","description":"Single entry from top_divergent_terms with resolved token."},"ResourceEventBatchRequest":{"properties":{"events":{"items":{"$ref":"#/components/schemas/ResourceEventRequest"},"type":"array","maxItems":100,"minItems":1,"title":"Events","description":"Events (max 100)"}},"type":"object","required":["events"],"title":"ResourceEventBatchRequest","description":"Request schema for batch resource event ingestion.\n\nIssue #238: Batch upsert/delete for efficiency.\n\nExample:\n    {\n        \"events\": [\n            {\"op\": \"upsert\", \"doc_id\": \"PROD-1\", \"version\": 1, \"payload\": {...}},\n            {\"op\": \"upsert\", \"doc_id\": \"PROD-2\", \"version\": 1, \"payload\": {...}},\n            {\"op\": \"delete\", \"doc_id\": \"PROD-999\", \"version\": 5}\n        ]\n    }"},"ResourceEventBatchResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"created_count":{"type":"integer","title":"Created Count","description":"Number of events created"},"failed_count":{"type":"integer","title":"Failed Count","description":"Number of events that failed","default":0},"event_ids":{"items":{"type":"integer"},"type":"array","title":"Event Ids","description":"Created event IDs"},"errors":{"items":{"additionalProperties":true,"type":"object"},"type":"array","title":"Errors","description":"Error details for failed events"}},"type":"object","required":["created_count"],"title":"ResourceEventBatchResponse","description":"Response schema for batch resource event ingestion."},"ResourceEventItem":{"properties":{"id":{"type":"integer","title":"Id","description":"Event sequence id (stable ordering)"},"op":{"type":"string","enum":["upsert","delete"],"title":"Op","description":"Event operation"},"doc_id":{"type":"string","title":"Doc Id","description":"Document identifier (stable across versions)"},"version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version","description":"Document version. NULL is a legitimate value for a delete-all-versions op (Issue #262)."},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At","description":"ISO-8601 UTC creation timestamp"}},"type":"object","required":["id","op","doc_id"],"title":"ResourceEventItem","description":"Single row in the recent ingest events list."},"ResourceEventRecord":{"properties":{"id":{"type":"string","title":"Id","description":"BigInt append-only event id (also the cursor key), serialized as a string so JS clients can't lose precision above 2^53-1"},"op":{"type":"string","title":"Op","description":"Operation: 'upsert' or 'delete'"},"doc_id":{"type":"string","title":"Doc Id","description":"Document identifier (stable across versions)"},"version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Version","description":"Document version; null for delete-all-versions"},"idempotency_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idempotency Key","description":"Client-provided deduplication key, if any"},"importance":{"type":"number","title":"Importance","description":"Importance score (0.0–1.0)"},"created_at":{"type":"string","title":"Created At","description":"Event creation time (ISO 8601 UTC)"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload","description":"JSONB payload; null for delete ops OR when omitted because it exceeds the inline size cap (see payload_truncated)"},"event_metadata":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Event Metadata","description":"Additional metadata (source, correlation_id, etc.)"},"payload_bytes":{"type":"integer","title":"Payload Bytes","description":"Serialized payload size in bytes (0 when null)"},"payload_truncated":{"type":"boolean","title":"Payload Truncated","description":"True when the payload exceeded the inline size cap and was omitted from this response"}},"type":"object","required":["id","op","doc_id","importance","created_at","payload_bytes","payload_truncated"],"title":"ResourceEventRecord","description":"A single ingest event row for the Resource Detail Data tab."},"ResourceEventRequest":{"properties":{"op":{"type":"string","pattern":"^(upsert|delete)$","title":"Op","description":"Operation: 'upsert' or 'delete'"},"doc_id":{"type":"string","maxLength":255,"minLength":1,"title":"Doc Id","description":"Document ID (stable across versions)"},"version":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Version","description":"Document version (NULL for delete-all-versions, >=1 for specific version)"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload","description":"Document payload (NULL for delete)"},"idempotency_key":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Idempotency Key","description":"Optional idempotency key for deduplication"},"event_metadata":{"additionalProperties":true,"type":"object","title":"Event Metadata","description":"Additional metadata (source, tenant, etc.)"},"importance":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Importance","description":"Memory importance score (0.0-1.0, default 0.6)"}},"type":"object","required":["op","doc_id"],"title":"ResourceEventRequest","description":"Request schema for resource event ingestion.\n\nIssue #238: Resource-driven incremental indexing.\n\nExample:\n    {\n        \"op\": \"upsert\",\n        \"doc_id\": \"PROD-12345\",\n        \"version\": 3,\n        \"payload\": {\n            \"product_name\": \"ワイヤレスイヤホン\",\n            \"price\": 5980,\n            \"category\": \"オーディオ\"\n        },\n        \"idempotency_key\": \"unique-client-key-123\"\n    }"},"ResourceEventResponse":{"properties":{"status":{"type":"string","title":"Status","default":"success"},"event_id":{"type":"integer","title":"Event Id","description":"Created event ID"},"queued":{"type":"boolean","title":"Queued","description":"Whether indexing is queued","default":true},"estimated_indexing_time_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Estimated Indexing Time Seconds","description":"Estimated time until indexed (seconds, None if unknown)"}},"type":"object","required":["event_id"],"title":"ResourceEventResponse","description":"Response schema for resource event ingestion."},"ResourceEventsResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/ResourceEventRecord"},"type":"array","title":"Events"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Opaque cursor for the next page; null on the last page"}},"type":"object","required":["events"],"title":"ResourceEventsResponse","description":"Cursor-paginated resource events response."},"ResourceImpactResponse":{"properties":{"resource_id":{"type":"string","title":"Resource Id"},"token_count":{"type":"integer","title":"Token Count","description":"Number of active tokens using this resource"},"memory_count":{"type":"integer","title":"Memory Count","description":"Number of existing memories from this resource"},"current_schema_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Schema Version","description":"Current schema version (null if no schema exists)"}},"type":"object","required":["resource_id","token_count","memory_count"],"title":"ResourceImpactResponse","description":"Response with resource change impact information.\n\nIssue #266: Schema change impact warnings."},"ResourceIngestStats":{"properties":{"total_events":{"type":"integer","title":"Total Events"},"last_n_days":{"type":"integer","title":"Last N Days"},"avg_per_day":{"type":"number","title":"Avg Per Day"},"active_tokens":{"type":"integer","title":"Active Tokens"},"timeline":{"items":{"$ref":"#/components/schemas/TimelineItem"},"type":"array","title":"Timeline"}},"type":"object","required":["total_events","last_n_days","avg_per_day","active_tokens","timeline"],"title":"ResourceIngestStats","description":"Resource Ingest API statistics.\n\nIssue #265: Public API usage stats."},"ResourceListItem":{"properties":{"resource_id":{"type":"string","title":"Resource Id","description":"Resource identifier"},"context_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Id","description":"UUID of the context bound to this resource. Null when every context of the resource has been deleted (#1863): such a row is listed only while the resource still has active tokens, so they stay reachable."},"context_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Name","description":"Context name (URL-safe identifier)"},"context_display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Context Display Name","description":"Human-readable context name"},"token_count":{"type":"integer","title":"Token Count","description":"Number of active resource tokens"},"memory_count":{"type":"integer","title":"Memory Count","description":"Number of non-deleted memories"},"current_schema_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Schema Version","description":"Latest schema version, or null if no schema registered"},"created_at":{"type":"string","title":"Created At","description":"Context creation time (ISO 8601 UTC)"},"updated_at":{"type":"string","title":"Updated At","description":"Most recent activity — GREATEST(last_event_at, context.updated_at, context.created_at) as ISO 8601 UTC"}},"type":"object","required":["resource_id","token_count","memory_count","created_at","updated_at"],"title":"ResourceListItem","description":"Single resource entry in the workspace resource list."},"ResourceListResponse":{"properties":{"resources":{"items":{"$ref":"#/components/schemas/ResourceListItem"},"type":"array","title":"Resources"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["resources","total"],"title":"ResourceListResponse","description":"Workspace resource list response."},"ResourceTokenCreate":{"properties":{"resource_id":{"type":"string","maxLength":255,"minLength":1,"title":"Resource Id","description":"Resource identifier this token is scoped to"},"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description","description":"Human-readable description"},"quota_events_per_hour":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Quota Events Per Hour","description":"Event ingestion quota per hour (default: 1000)","default":1000}},"type":"object","required":["resource_id"],"title":"ResourceTokenCreate","description":"Request model for creating a resource token."},"ResourceTokenCreateResponse":{"properties":{"id":{"title":"Id","description":"Public id of the token (`rtok_` + 22 base62 characters)"},"resource_id":{"title":"Resource Id","description":"Resource identifier"},"description":{"title":"Description","description":"Human-readable description"},"quota_events_per_hour":{"title":"Quota Events Per Hour","description":"Event ingestion quota per hour"},"created_by":{"title":"Created By","description":"User ID who created this token"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"is_active":{"title":"Is Active","description":"Whether token is active"},"status":{"title":"Status","description":"Current status"},"token":{"title":"Token","description":"Plaintext resource token (ONLY shown once - must be saved by client)"}},"type":"object","required":["id","resource_id","quota_events_per_hour","created_at","is_active","status","token"],"title":"ResourceTokenCreateResponse","description":"Response model for resource token creation (includes plaintext token).\n\nWARNING: The `token` field is shown ONLY once. Client must save it immediately."},"ResourceTokenResponse":{"properties":{"id":{"title":"Id","description":"Public id of the token (`rtok_` + 22 base62 characters)"},"resource_id":{"title":"Resource Id","description":"Resource identifier"},"description":{"title":"Description","description":"Human-readable description"},"quota_events_per_hour":{"title":"Quota Events Per Hour","description":"Event ingestion quota per hour"},"created_by":{"title":"Created By","description":"User ID who created this token"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"is_active":{"title":"Is Active","description":"Whether token is active"},"status":{"title":"Status","description":"Current status"}},"type":"object","required":["id","resource_id","quota_events_per_hour","created_at","is_active","status"],"title":"ResourceTokenResponse","description":"Response model for resource token metadata (no plaintext)."},"ResourceTokenUpdate":{"properties":{"description":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Description","description":"Updated description"},"quota_events_per_hour":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Quota Events Per Hour","description":"Updated quota (1-10000)"}},"type":"object","title":"ResourceTokenUpdate","description":"Request model for updating a resource token."},"RevokeGrant":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"recipient_pubkey_id":{"type":"string","format":"uuid","title":"Recipient Pubkey Id"}},"type":"object","required":["name","recipient_pubkey_id"],"title":"RevokeGrant","description":"Revoke one recipient's grant on a secret (flags rotation_needed)."},"RevokeRequest":{"properties":{"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"RevokeRequest","description":"Revocation payload.\n\n``reason`` is required — not optional-with-a-default. Revoking shrinks a\nlive quota, so there is no non-destructive case in which an empty reason\nwould be acceptable (contrast ``admin.py``'s conditional-reason rule for\n``workspace_slot_bonus``, where a non-negative delta is harmless)."},"RoleUpdateRequest":{"properties":{"role":{"type":"string","title":"Role"}},"type":"object","required":["role"],"title":"RoleUpdateRequest","description":"Request to update user role."},"RotateKmcKeyResponse":{"properties":{"connector_id":{"title":"Connector Id"},"kmc_api_key":{"title":"Kmc Api Key","description":"New plaintext KMC write key; save immediately"},"kmc_api_key_expires_at":{"title":"Kmc Api Key Expires At"},"config_version":{"title":"Config Version"}},"type":"object","required":["connector_id","kmc_api_key","kmc_api_key_expires_at","config_version"],"title":"RotateKmcKeyResponse","description":"Response after rotating a connector's KMC write key. Shown exactly once."},"SchemaCreateRequest":{"properties":{"resource_id":{"type":"string","maxLength":255,"minLength":1,"title":"Resource Id","description":"Resource identifier"},"field_definitions":{"items":{"$ref":"#/components/schemas/FieldDefinition"},"type":"array","maxItems":100,"minItems":1,"title":"Field Definitions","description":"Field metadata (max 100 fields)"}},"type":"object","required":["resource_id","field_definitions"],"title":"SchemaCreateRequest","description":"Request to create a new resource schema."},"SchemaResponse":{"properties":{"resource_id":{"type":"string","title":"Resource Id"},"schema_version":{"type":"integer","title":"Schema Version"},"field_definitions":{"items":{"$ref":"#/components/schemas/FieldDefinition"},"type":"array","title":"Field Definitions"},"created_at":{"type":"string","title":"Created At"}},"type":"object","required":["resource_id","schema_version","field_definitions","created_at"],"title":"SchemaResponse","description":"Response with resource schema."},"SearchTimelineItem":{"properties":{"date":{"type":"string","title":"Date"},"total":{"type":"integer","title":"Total"},"anonymous":{"type":"integer","title":"Anonymous"},"authenticated":{"type":"integer","title":"Authenticated"}},"type":"object","required":["date","total","anonymous","authenticated"],"title":"SearchTimelineItem","description":"Search timeline data point with anonymous/authenticated split.\n\nIssue #265: P1-6 - Type-safe timeline."},"SecretFetch":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"version_number":{"anyOf":[{"type":"integer","minimum":1.0},{"type":"null"}],"title":"Version Number","description":"Pin a version; omit for latest"}},"type":"object","required":["name"],"title":"SecretFetch","description":"Fetch a secret's ciphertext (name carried in body to allow '/' names)."},"SecretMetaResponse":{"properties":{"name":{"title":"Name"},"status":{"title":"Status"},"rotation_needed":{"title":"Rotation Needed"},"current_version":{"title":"Current Version"},"grant_count":{"title":"Grant Count"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["name","status","rotation_needed","current_version","grant_count","created_at","updated_at"],"title":"SecretMetaResponse","description":"Secret metadata — never includes the value."},"SecretPut":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"ciphertext":{"type":"string","maxLength":262144,"minLength":1,"title":"Ciphertext","description":"Armored age ciphertext"},"recipients_snapshot":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Recipients Snapshot","description":"Fingerprints the ciphertext was encrypted to"},"grant_pubkey_ids":{"items":{"type":"string","format":"uuid"},"type":"array","minItems":1,"title":"Grant Pubkey Ids","description":"Recipient pubkey ids to grant (must be active)"}},"type":"object","required":["name","ciphertext","recipients_snapshot","grant_pubkey_ids"],"title":"SecretPut","description":"Store a new ciphertext version of a secret. Server never sees plaintext."},"SecretPutResponse":{"properties":{"name":{"title":"Name"},"version_number":{"title":"Version Number"},"status":{"title":"Status"},"rotation_needed":{"title":"Rotation Needed"}},"type":"object","required":["name","version_number","status","rotation_needed"],"title":"SecretPutResponse","description":"Result of a put."},"SecretValueResponse":{"properties":{"name":{"title":"Name"},"version_number":{"title":"Version Number"},"alg":{"title":"Alg"},"ciphertext":{"title":"Ciphertext"},"blob_ref":{"title":"Blob Ref"},"recipients_snapshot":{"title":"Recipients Snapshot"},"rotation_needed":{"title":"Rotation Needed"},"created_at":{"title":"Created At"}},"type":"object","required":["name","version_number","alg","ciphertext","blob_ref","recipients_snapshot","rotation_needed","created_at"],"title":"SecretValueResponse","description":"Opaque ciphertext returned to a granted caller. The server cannot read it."},"SelectorReport":{"properties":{"applied":{"type":"boolean","title":"Applied"},"model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model"},"kept":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Kept"},"dropped":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dropped"},"dropped_memory_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Dropped Memory Ids"},"reason":{"anyOf":[{"type":"string","enum":["timeout","provider_error","invalid_answer","daily_cap","too_large","no_provider","counter_unavailable","inactive"]},{"type":"null"}],"title":"Reason"}},"type":"object","required":["applied"],"title":"SelectorReport","description":"#2144: what the recall LLM selector did to this page.\n\n``applied: true`` carries ``model``, ``kept`` / ``dropped`` (results left\nand removed) and ``dropped_memory_ids`` (at most 20, page order);\n``applied: false`` carries only ``reason`` and the page is the unselected\none. Serialized without the unset keys."},"ServiceStatus":{"properties":{"status":{"type":"string","title":"Status"},"version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"type":"object","required":["status"],"title":"ServiceStatus","description":"Individual service status."},"ShareKeyCreate":{"properties":{"name":{"type":"string","maxLength":100,"minLength":1,"title":"Name","description":"Friendly name"},"context_id":{"type":"string","format":"uuid","title":"Context Id","description":"The single context to confine the key to (must be owned)"},"ttl_days":{"anyOf":[{"type":"integer","maximum":3650.0,"minimum":1.0},{"type":"null"}],"title":"Ttl Days","description":"Requested lifetime in days. Clamped server-side to a 30-day ceiling; omit for the 30-day default."}},"type":"object","required":["name","context_id"],"title":"ShareKeyCreate","description":"Request to mint a share key bound to one owned context."},"ShareKeyCreateResponse":{"properties":{"id":{"title":"Id","description":"Public id of the share key (`skey_` + 22 base62 characters)"},"key_prefix":{"title":"Key Prefix","description":"First 16 characters of the key (display only)"},"name":{"title":"Name","description":"Friendly name"},"user_id":{"title":"User Id","description":"Minting owner"},"context_id":{"title":"Context Id","description":"The single context this key is confined to"},"scope":{"title":"Scope","description":"Always 'memory:read'"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"revoked_at":{"title":"Revoked At","description":"Revocation timestamp"},"expires_at":{"title":"Expires At","description":"Expiry timestamp (always set)"},"status":{"title":"Status","description":"Current status"},"share_key":{"title":"Share Key","description":"Plaintext share key (ONLY shown once — must be saved by the client)"}},"type":"object","required":["id","key_prefix","name","user_id","context_id","scope","created_at","expires_at","status","share_key"],"title":"ShareKeyCreateResponse","description":"Mint response — includes the plaintext key ONCE."},"ShareKeyResponse":{"properties":{"id":{"title":"Id","description":"Public id of the share key (`skey_` + 22 base62 characters)"},"key_prefix":{"title":"Key Prefix","description":"First 16 characters of the key (display only)"},"name":{"title":"Name","description":"Friendly name"},"user_id":{"title":"User Id","description":"Minting owner"},"context_id":{"title":"Context Id","description":"The single context this key is confined to"},"scope":{"title":"Scope","description":"Always 'memory:read'"},"created_at":{"title":"Created At","description":"Creation timestamp"},"last_used_at":{"title":"Last Used At","description":"Last usage timestamp"},"revoked_at":{"title":"Revoked At","description":"Revocation timestamp"},"expires_at":{"title":"Expires At","description":"Expiry timestamp (always set)"},"status":{"title":"Status","description":"Current status"}},"type":"object","required":["id","key_prefix","name","user_id","context_id","scope","created_at","expires_at","status"],"title":"ShareKeyResponse","description":"Share-key metadata (never includes the secret)."},"SharedSessionState":{"properties":{"key":{"title":"Key","description":"The agent-state key (e.g. a thread id)"},"status":{"title":"Status","description":"Extracted from value.status when the value is an object; else null"},"awaiting_approval":{"title":"Awaiting Approval","description":"True when status == 'awaiting_approval' — surfaces operator-gated work"},"updated_at":{"title":"Updated At","description":"Checkpoint time of this entry — the consumer treats this as the *historical* read time (memory-cloud), not 'live now' (the cockpit is authoritative for now). Relates to #1047 staleness honesty."},"value":{"title":"Value","description":"The raw checkpointed run-state value"}},"type":"object","required":["key","awaiting_approval","updated_at","value"],"title":"SharedSessionState","description":"One agent session-state entry, projected for read-only observation (#1064)."},"SharedSessionsResponse":{"properties":{"sessions":{"title":"Sessions","description":"Live entries, most recent first"},"count":{"title":"Count","description":"Number of live entries"},"as_of":{"title":"As Of","description":"Server time this snapshot was read"}},"type":"object","required":["sessions","count","as_of"],"title":"SharedSessionsResponse","description":"The bound context's live agent session-state, for a status board (#1064)."},"SignupGateConfigResponse":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"mode":{"type":"string","enum":["manual","github_sponsors","both"],"title":"Mode"},"github_sponsors_grace_period_days":{"type":"integer","title":"Github Sponsors Grace Period Days"}},"type":"object","required":["enabled","mode","github_sponsors_grace_period_days"],"title":"SignupGateConfigResponse","description":"Config read-model.\n\n``github_sponsors_grace_period_days`` is exposed for Phase 2 UX affordance\neven though Phase 1 doesn't act on it; showing the value keeps the\nadmin UI honest about what will happen once Sponsors mode activates."},"SignupGateConfigUpdate":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"mode":{"type":"string","const":"manual","title":"Mode"}},"type":"object","required":["enabled","mode"],"title":"SignupGateConfigUpdate"},"SleepActionItem":{"properties":{"id":{"title":"Id"},"phase":{"title":"Phase"},"action_type":{"title":"Action Type"},"memory_id":{"title":"Memory Id"},"target_id":{"title":"Target Id"},"details":{"title":"Details"},"created_at":{"title":"Created At"}},"type":"object","required":["id","phase","action_type","memory_id","target_id","details","created_at"],"title":"SleepActionItem","description":"Sleep action audit log entry."},"SleepContextsUsage":{"properties":{"used":{"type":"integer","title":"Used","description":"Contexts with sleep_mode != 'skip' in this workspace","default":0},"limit":{"type":"integer","title":"Limit","description":"Plan + addon effective limit","default":0},"addon_bonus":{"type":"integer","title":"Addon Bonus","description":"Addon-supplied bonus (extra_sleep_contexts)","default":0},"remaining":{"type":"integer","title":"Remaining","description":"max(0, limit - used)","default":0}},"type":"object","title":"SleepContextsUsage","description":"Sleep-enabled contexts quota usage (Issue #560).\n\nThis is the dashboard READ shape — ``used`` / ``limit`` / ``remaining``\nfor showing \"X / Y\" in the UI. The 429 quota-exceeded body raised by\n``ContextService._assert_sleep_quota_or_raise`` uses a parallel-but-\ndistinct shape (``current`` / ``requested`` / ``limit`` / ``addon_bonus``)\noptimized for the action-rejection case (\"you tried to enable one more,\nhere's the new total\"). Both surfaces share ``limit`` and ``addon_bonus``;\nthe read surface adds ``remaining`` (= ``max(0, limit - used)``) for direct\ndisplay, and the error surface adds ``requested`` (= ``current + 1``) for\n\"how many would there be.\" Kept distinct so neither has to carry fields it\ndoes not need."},"SleepReportDetail":{"properties":{"id":{"title":"Id"},"user_id":{"title":"User Id"},"workspace_id":{"title":"Workspace Id"},"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"user_email":{"title":"User Email"},"status":{"title":"Status"},"started_at":{"title":"Started At"},"completed_at":{"title":"Completed At"},"memories_processed":{"title":"Memories Processed"},"edges_created":{"title":"Edges Created"},"memories_merged":{"title":"Memories Merged"},"memories_promoted":{"title":"Memories Promoted"},"memories_flagged":{"title":"Memories Flagged"},"llm_calls_made":{"title":"Llm Calls Made"},"llm_tokens_used":{"title":"Llm Tokens Used"},"llm_call_failures":{"title":"Llm Call Failures"},"context_deleted":{"title":"Context Deleted"},"embedding_calls_made":{"title":"Embedding Calls Made"},"error_message":{"title":"Error Message"},"edge_discovery_result":{"title":"Edge Discovery Result"},"dedup_result":{"title":"Dedup Result"},"merge_retention_result":{"title":"Merge Retention Result"},"importance_result":{"title":"Importance Result"},"consolidation_result":{"title":"Consolidation Result"},"reindex_result":{"title":"Reindex Result"}},"type":"object","required":["id","user_id","workspace_id","context_id","status","started_at","completed_at","memories_processed","edges_created","memories_merged","memories_promoted","memories_flagged","llm_calls_made","llm_tokens_used","embedding_calls_made","error_message","edge_discovery_result","dedup_result","importance_result","consolidation_result","reindex_result"],"title":"SleepReportDetail","description":"Sleep report full detail."},"SleepReportDetailResponse":{"properties":{"report":{"$ref":"#/components/schemas/SleepReportDetail"},"actions":{"items":{"$ref":"#/components/schemas/SleepActionItem"},"type":"array","title":"Actions"},"action_count":{"type":"integer","title":"Action Count"}},"type":"object","required":["report","actions","action_count"],"title":"SleepReportDetailResponse","description":"Sleep report detail with all actions."},"SleepReportListResponse":{"properties":{"reports":{"items":{"$ref":"#/components/schemas/SleepReportSummary"},"type":"array","title":"Reports"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["reports","total","limit","offset"],"title":"SleepReportListResponse","description":"List of sleep reports with pagination."},"SleepReportSummary":{"properties":{"id":{"title":"Id"},"user_id":{"title":"User Id"},"workspace_id":{"title":"Workspace Id"},"context_id":{"title":"Context Id"},"context_name":{"title":"Context Name"},"user_email":{"title":"User Email"},"status":{"title":"Status"},"started_at":{"title":"Started At"},"completed_at":{"title":"Completed At"},"memories_processed":{"title":"Memories Processed"},"edges_created":{"title":"Edges Created"},"memories_merged":{"title":"Memories Merged"},"memories_promoted":{"title":"Memories Promoted"},"memories_flagged":{"title":"Memories Flagged"},"llm_calls_made":{"title":"Llm Calls Made"},"llm_tokens_used":{"title":"Llm Tokens Used"},"llm_call_failures":{"title":"Llm Call Failures"}},"type":"object","required":["id","user_id","workspace_id","context_id","status","started_at","completed_at","memories_processed","edges_created","memories_merged","memories_promoted","memories_flagged","llm_calls_made","llm_tokens_used"],"title":"SleepReportSummary","description":"Sleep report summary for list view."},"SleepRunRequest":{"properties":{"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}},"type":"object","title":"SleepRunRequest","description":"Body for POST /admin/sleep/run.\n\nAttributes:\n    context_id: Target context. When null, runs against every context\n        owned by the calling admin (self-scoped)."},"SleepRunResponse":{"properties":{"report_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Report Ids"}},"type":"object","required":["report_ids"],"title":"SleepRunResponse","description":"202 response for POST /admin/sleep/run.\n\nAttributes:\n    report_ids: IDs of the newly-created sleep reports, one per\n        scheduled context. For a single-context request this list\n        always has exactly one element."},"SpanRef":{"properties":{"start":{"type":"integer","title":"Start"},"end":{"type":"integer","title":"End"}},"type":"object","required":["start","end"],"title":"SpanRef","description":"#2144: one verbatim passage of a recall row's stored content.\n\n``start`` / ``end`` are character offsets into the memory's stored\ncontent (end exclusive) — ``reference(memory_id, content_offset=start)``\nreads from there."},"SpendCapValues":{"properties":{"tier_default_daily_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tier Default Daily Usd"},"tier_default_monthly_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Tier Default Monthly Usd"},"override_daily_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Override Daily Usd"},"override_monthly_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Override Monthly Usd"},"effective_daily_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Effective Daily Usd"},"effective_monthly_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Effective Monthly Usd"},"current_daily_usd":{"type":"number","title":"Current Daily Usd","default":0.0},"current_monthly_usd":{"type":"number","title":"Current Monthly Usd","default":0.0}},"type":"object","title":"SpendCapValues","description":"BYOK embedding spend cap values (Issue #709).\n\nAll amounts are USD floats; ``None`` means \"uncapped\". Both the override\n(per-workspace, on ``Workspace``) and the tier default (on ``PlanTier``)\nare surfaced so the admin UI can render \"override / tier default /\neffective\" the same way the addon breakdown does for additive quotas."},"SupersedeCandidate":{"properties":{"memory_id":{"title":"Memory Id"},"summary":{"title":"Summary"},"similarity":{"title":"Similarity"},"detected_at":{"title":"Detected At"},"verified":{"title":"Verified"}},"type":"object","required":["memory_id","summary","similarity"],"title":"SupersedeCandidate","description":"#1403: a near-duplicate the memory likely supersedes (a suggestion — never\nan auto-created edge).\n\nDetected at ingest by the k-NN seeding (top-1 neighbor at cosine >=\n_SUPERSEDE_SUGGEST_THRESHOLD) and stored on the new memory. Surfaced —\nliveness-guarded — on recall()/reference() so a client can offer a\nconfirm→create_edge(edge_type=\"supersedes\") action. It disappears on its own\nonce the target is deleted (self-healing) or the suggestion is accepted (the\nstored key is cleared when the matching supersedes edge is created). The\n#1208 over-supersede prevention is preserved: the edge is only ever created\non explicit user/agent confirmation, never by this suggestion."},"SystemAdminListResponse":{"properties":{"admins":{"items":{"$ref":"#/components/schemas/UserWithAdminFlag"},"type":"array","title":"Admins"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["admins","total"],"title":"SystemAdminListResponse","description":"Response for listing system administrators.\n\nIssue #166: System Admin management API.\nIssue #1813: ``initial_admin_id`` (integer PK) dropped — each entry's\n``is_initial_admin`` flag marks the protected initial admin."},"TelemetryResponse":{"properties":{"services":{"additionalProperties":{"$ref":"#/components/schemas/ServiceStatus"},"type":"object","title":"Services"},"embedding_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Embedding Config"},"memory_stats":{"additionalProperties":true,"type":"object","title":"Memory Stats"},"neural_memory":{"additionalProperties":true,"type":"object","title":"Neural Memory"},"uptime_seconds":{"type":"integer","title":"Uptime Seconds"},"version":{"type":"string","title":"Version"}},"type":"object","required":["services","memory_stats","neural_memory","uptime_seconds","version"],"title":"TelemetryResponse","description":"System telemetry response."},"TermsAcceptanceRequest":{"properties":{"version":{"type":"string","maxLength":64,"minLength":1,"title":"Version"}},"type":"object","required":["version"],"title":"TermsAcceptanceRequest","description":"The version the user just agreed to — must be the current one."},"TermsAcceptanceResponse":{"properties":{"version":{"type":"string","title":"Version"},"recorded":{"type":"boolean","title":"Recorded"},"terms_acceptance_required":{"type":"boolean","title":"Terms Acceptance Required"}},"type":"object","required":["version","recorded","terms_acceptance_required"],"title":"TermsAcceptanceResponse","description":"``recorded`` is false when this version was already accepted."},"TierDowngradeEligibility":{"properties":{"target_plan":{"type":"string","title":"Target Plan"},"eligible":{"type":"boolean","title":"Eligible"},"blockers":{"items":{"$ref":"#/components/schemas/DowngradeBlocker"},"type":"array","title":"Blockers"}},"type":"object","required":["target_plan","eligible","blockers"],"title":"TierDowngradeEligibility","description":"Whether the workspace's usage fits one specific lower tier."},"TimelineItem":{"properties":{"date":{"type":"string","title":"Date"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["date","count"],"title":"TimelineItem","description":"Timeline data point.\n\nIssue #265: P1-6 - Type-safe timeline."},"TokenIntrospectionResponse":{"properties":{"active":{"type":"boolean","title":"Active"},"client_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Id"},"scope":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scope"},"exp":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Exp"},"iat":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Iat"},"token_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Type"},"aud":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Aud"}},"type":"object","required":["active"],"title":"TokenIntrospectionResponse","description":"OAuth 2.0 Token Introspection Response (RFC 7662).\n\nIssue #157: Type-safe response model for /introspect endpoint.\n\nAttributes:\n    active: Whether token is active\n    client_id: Client identifier (if active)\n    scope: Granted scopes (if active)\n    exp: Expiration timestamp (if active)\n    iat: Issued-at timestamp (if active)\n    token_type: Token type (if active)\n    aud: Audience/Resource (RFC 8707, if present)"},"TransferOwnershipRequest":{"properties":{"target_user_id":{"type":"string","maxLength":255,"minLength":1,"title":"Target User Id","description":"User ID of an existing workspace member to promote to owner."}},"type":"object","required":["target_user_id"],"title":"TransferOwnershipRequest","description":"Request to transfer workspace ownership to an existing member."},"TransferOwnershipResponse":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"previous_owner_id":{"type":"string","title":"Previous Owner Id"},"new_owner_id":{"type":"string","title":"New Owner Id"},"ownership_epoch":{"type":"integer","title":"Ownership Epoch"},"changed":{"type":"boolean","title":"Changed","description":"False when the target already owned the workspace (no-op)."}},"type":"object","required":["workspace_id","previous_owner_id","new_owner_id","ownership_epoch","changed"],"title":"TransferOwnershipResponse","description":"Result of an ownership transfer (or idempotent no-op)."},"UndoMergeResponse":{"properties":{"restored_memory_id":{"type":"string","title":"Restored Memory Id"},"winner_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Winner Id"},"report_id":{"type":"string","title":"Report Id"},"undone_action_id":{"type":"integer","title":"Undone Action Id"}},"type":"object","required":["restored_memory_id","winner_id","report_id","undone_action_id"],"title":"UndoMergeResponse","description":"200 response for POST /admin/sleep/actions/{action_id}/undo-merge.\n\nAttributes:\n    restored_memory_id: The merge loser brought back into recall.\n    winner_id: The merge winner (unchanged by the undo).\n    report_id: The sleep report whose audit log records both the merge\n        and this undo.\n    undone_action_id: The original merge action that was undone."},"UnlinkProviderRequest":{"properties":{"provider":{"type":"string","enum":["google","github"],"title":"Provider"}},"type":"object","required":["provider"],"title":"UnlinkProviderRequest","description":"Body for POST /me/account/unlink-provider."},"UnlinkProviderResponse":{"properties":{"status":{"type":"string","title":"Status"}},"type":"object","required":["status"],"title":"UnlinkProviderResponse","description":"Returned by POST /me/account/unlink-provider on success."},"UpdateAddonRequest":{"properties":{"addon_memory_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Memory Bonus"},"addon_mcp_quota_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Mcp Quota Bonus"},"addon_member_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Member Bonus"},"addon_context_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Context Bonus"},"addon_analysis_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Analysis Bonus"},"addon_rest_quota_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Rest Quota Bonus"},"addon_public_quota_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Public Quota Bonus"},"addon_storage_bonus_mb":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Storage Bonus Mb"},"addon_sleep_contexts_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Sleep Contexts Bonus"},"addon_connector_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Connector Bonus"},"addon_memories_per_day_bonus":{"anyOf":[{"type":"integer","maximum":2000000000.0,"minimum":0.0},{"type":"null"}],"title":"Addon Memories Per Day Bonus"}},"type":"object","title":"UpdateAddonRequest","description":"Request to update workspace addon bonuses (Issue #665).\n\nAll 9 fields are **optional with no-touch semantics**: when a field is\nomitted from the request body, the corresponding admin grant is left\nuntouched. To zero out an addon, the client must send the field\nexplicitly with value ``0``.\n\nConcrete values are bounded ``0 <= value <= 2_000_000_000``. The upper\nbound is below PostgreSQL ``INTEGER`` max (2_147_483_647) so the\nmultiplied-out cache-column write at recalc time never overflows.\nPer-handler logic additionally rejects values lower than the active\nStripe-purchased SUM for the same addon type (HTTP 400) so silent\nclamping cannot happen.\n\nWire-format compatibility note (#665 review fix #2): pre-#665 the 5\nlegacy fields defaulted to ``int = Field(0)`` so omitting them meant\n\"set to zero\". That semantics is incompatible with the row-based SSoT\ndesign — an omitted-default 0 would silently DELETE existing\nWorkspaceAddon admin_grant rows on partial-update PUTs. The unified\noptional shape closes this footgun. Clients that intentionally want\nto zero a field continue to work by sending the field explicitly with\nvalue 0; clients that omit the field get the safer no-touch behavior."},"UpdateContextMemberRoleRequest":{"properties":{"role":{"type":"string","pattern":"^(owner|editor|viewer)$","title":"Role"}},"type":"object","required":["role"],"title":"UpdateContextMemberRoleRequest","description":"Request model for updating context member role."},"UpdateMemberContextAccessRequest":{"properties":{"allowed_context_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Allowed Context Ids","description":"List of context IDs the member can access. null = no restriction (all contexts). [] = no context access. [uuid1, uuid2, ...] = only these contexts."}},"type":"object","title":"UpdateMemberContextAccessRequest","description":"Request model for updating member's context access.\n\nIssue #234: Context access restriction for member/viewer."},"UpdateMemberRoleRequest":{"properties":{"role":{"type":"string","pattern":"^(owner|admin|member|viewer)$","title":"Role"}},"type":"object","required":["role"],"title":"UpdateMemberRoleRequest","description":"Request model for updating member role."},"UpdateSpendCapRequest":{"properties":{"embedding_daily_cap_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Embedding Daily Cap Usd"},"embedding_monthly_cap_usd":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Embedding Monthly Cap Usd"}},"type":"object","title":"UpdateSpendCapRequest","description":"Request to update the per-workspace embedding spend cap (Issue #709).\n\nBoth fields are optional: ``None`` means \"remove the per-workspace\noverride and fall back to the tier default\" (NOT \"uncap\"). To genuinely\nuncap a workspace below its tier default, an admin would need to bump\nthe workspace to a tier with no default cap (or override the tier's\ncap via ``plan_*_embedding_*_cap_usd`` env vars).\n\nValues are bounded ``>= 0`` (CHECK constraint enforces same on the DB);\nthe route handler additionally rejects values above the workspace's\ncurrent tier default to keep tier-bounded edit affordance honest."},"UpdateUserProfileRequest":{"properties":{"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"timezone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Timezone","description":"IANA timezone (e.g., Asia/Tokyo, America/New_York)"},"locale":{"anyOf":[{"type":"string","pattern":"^(en|ja)$"},{"type":"null"}],"title":"Locale","description":"UI language (en, ja)"}},"type":"object","title":"UpdateUserProfileRequest","description":"Request schema for updating user profile.\n\nIssue #175: User timezone settings\nIssue #221: i18n support (locale)"},"UpdateWorkspaceSlotBonusRequest":{"properties":{"delta":{"type":"integer","maximum":1000000.0,"minimum":-1000000.0,"title":"Delta","description":"Signed increment (±1M sanity bound; frontend uses ±1)."},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason","description":"Free-form reason (required for destructive over-cap ops)."}},"type":"object","required":["delta"],"title":"UpdateWorkspaceSlotBonusRequest","description":"Body for PATCH /admin/users/{user_id}/workspace_slot_bonus (#676).\n\n``delta`` is the signed increment to apply atomically. The ±1M bound is a\nsanity limit (frontend only sends ±1; the cap protects against admin\ntypos and against INT32 overflow on `workspace_slot_bonus + delta`).\n``reason`` is required only when the resulting cap would fall below the\nuser's current owned_count (a destructive admin operation); otherwise it\nmay be ``None``. The 500-char cap matches the audit_log payload budget."},"UpdateWorkspaceSlotBonusResponse":{"properties":{"before_value":{"type":"integer","title":"Before Value"},"after_value":{"type":"integer","title":"After Value"},"owned_count":{"type":"integer","title":"Owned Count"},"base_cap":{"type":"integer","title":"Base Cap"},"cap":{"type":"integer","title":"Cap"},"is_at_cap":{"type":"boolean","title":"Is At Cap"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["before_value","after_value","owned_count","base_cap","cap","is_at_cap","reason"],"title":"UpdateWorkspaceSlotBonusResponse","description":"Response for PATCH /admin/users/{user_id}/workspace_slot_bonus (#676).\n\nReturns enough state for the frontend to update its local view without a\nrefetch round-trip: the bonus before/after, plus the recomputed cap\nsummary. ``reason`` is echoed back so the optimistic UI can include it in\nthe success toast."},"UsageBreakdownResponse":{"properties":{"by_endpoint":{"items":{"$ref":"#/components/schemas/EndpointUsage"},"type":"array","title":"By Endpoint","description":"Usage by endpoint"},"total_requests":{"type":"integer","minimum":0.0,"title":"Total Requests","description":"Total requests"},"period_days":{"type":"integer","title":"Period Days","description":"Number of days in period"}},"type":"object","required":["by_endpoint","total_requests","period_days"],"title":"UsageBreakdownResponse","description":"Usage breakdown by endpoint."},"UsageCurrentResponse":{"properties":{"plan":{"$ref":"#/components/schemas/PlanLimits"},"usage":{"$ref":"#/components/schemas/CurrentUsage"},"memory_usage":{"$ref":"#/components/schemas/UsageStatus"},"daily_api_usage":{"$ref":"#/components/schemas/UsageStatus"},"weekly_api_usage":{"$ref":"#/components/schemas/UsageStatus"},"memories_today_usage":{"anyOf":[{"$ref":"#/components/schemas/UsageStatus"},{"type":"null"}],"description":"Memories created today vs the daily quota (Issue #1549)"},"capacity_lock":{"anyOf":[{"$ref":"#/components/schemas/CapacityLockInfo"},{"type":"null"}],"description":"Present when the workspace is over its Free plan capacity: search and saving are paused until it is cleaned up or re-subscribed (#1941)"}},"type":"object","required":["plan","usage","memory_usage","daily_api_usage","weekly_api_usage"],"title":"UsageCurrentResponse","description":"Current usage vs limits response."},"UsageHistoryResponse":{"properties":{"daily_stats":{"items":{"$ref":"#/components/schemas/DailyUsage"},"type":"array","title":"Daily Stats","description":"Daily usage breakdown"},"total_requests":{"type":"integer","minimum":0.0,"title":"Total Requests","description":"Total requests in period"},"period_start":{"type":"string","title":"Period Start","description":"Start date (YYYY-MM-DD)"},"period_end":{"type":"string","title":"Period End","description":"End date (YYYY-MM-DD)"}},"type":"object","required":["daily_stats","total_requests","period_start","period_end"],"title":"UsageHistoryResponse","description":"Historical usage data."},"UsageStatus":{"properties":{"current":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Current","description":"Current value"},"limit":{"anyOf":[{"type":"integer"},{"type":"number"}],"title":"Limit","description":"Limit value"},"percentage":{"type":"number","title":"Percentage","description":"Usage percentage (0-100+)"},"is_warning":{"type":"boolean","title":"Is Warning","description":"True if >= 80%"},"is_critical":{"type":"boolean","title":"Is Critical","description":"True if >= 95%"},"is_exceeded":{"type":"boolean","title":"Is Exceeded","description":"True if >= 100%"}},"type":"object","required":["current","limit","percentage","is_warning","is_critical","is_exceeded"],"title":"UsageStatus","description":"Usage status with percentage."},"UsageValues":{"properties":{"memories":{"type":"integer","title":"Memories"},"contexts":{"type":"integer","title":"Contexts"},"members":{"type":"integer","title":"Members"}},"type":"object","required":["memories","contexts","members"],"title":"UsageValues","description":"Current usage values."},"UserActivityItem":{"properties":{"user_id":{"title":"User Id"},"user_name":{"title":"User Name"},"user_email":{"title":"User Email"},"api_calls":{"title":"Api Calls"},"last_activity":{"title":"Last Activity"}},"type":"object","required":["user_id","user_name","user_email","api_calls","last_activity"],"title":"UserActivityItem","description":"User activity statistics item.\n\nIssue #249: Per-user activity breakdown."},"UserInfo":{"properties":{"id":{"title":"Id"},"email":{"title":"Email"},"name":{"title":"Name"},"picture":{"title":"Picture"},"role":{"title":"Role"},"created_at":{"title":"Created At"},"last_login":{"title":"Last Login"},"memory_count":{"title":"Memory Count"},"is_active":{"title":"Is Active"},"timezone":{"title":"Timezone"},"auth_provider":{"title":"Auth Provider"},"workspaces":{"title":"Workspaces"},"owned_count":{"title":"Owned Count"},"workspace_slot_bonus":{"title":"Workspace Slot Bonus"},"base_cap":{"title":"Base Cap"},"cap":{"title":"Cap"}},"type":"object","required":["id","email","name","role","created_at","memory_count","is_active"],"title":"UserInfo","description":"User information for admin view.\n\nIssue #164: Extended with workspaces field.\nIssue #175: Added timezone field for user preferences.\nIssue #695: Added owned_count / workspace_slot_bonus / base_cap / cap so\nthe /admin/users list can surface each user's owned-workspace usage\nagainst their cap without a follow-up per-user detail fetch. Field names\nintentionally mirror ``WorkspaceSummary`` (the detail endpoint's payload)\nso client code and mental models stay consistent. Defaults keep the\nschema additive — older consumers that ignore these fields still work."},"UserListResponse":{"properties":{"users":{"items":{"$ref":"#/components/schemas/UserInfo"},"type":"array","title":"Users"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["users","total"],"title":"UserListResponse","description":"Response for user list."},"UserProfileResponse":{"properties":{"user_id":{"title":"User Id"},"email":{"title":"Email"},"name":{"title":"Name"},"picture":{"title":"Picture"},"timezone":{"title":"Timezone"},"locale":{"title":"Locale"},"role":{"title":"Role"},"current_workspace_id":{"title":"Current Workspace Id"},"created_at":{"title":"Created At"},"last_login_at":{"title":"Last Login At"},"auth_method":{"title":"Auth Method"},"auth_provider":{"title":"Auth Provider"}},"type":"object","required":["user_id","email","name","picture","timezone","locale","role","current_workspace_id","created_at","last_login_at","auth_method"],"title":"UserProfileResponse","description":"Response schema for user profile.\n\nIssue #175: User timezone settings\nIssue #221: i18n support (locale)\nIssue #514: Expose auth_method + auth_provider for sign-in-method display\nIssue #1813: no integer ``id`` — the user is identified by ``user_id``"},"UserResponse":{"properties":{"email":{"type":"string","title":"Email"},"user_id":{"type":"string","title":"User Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"picture":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Picture"},"role":{"type":"string","title":"Role"},"timezone":{"type":"string","title":"Timezone","default":"UTC"}},"type":"object","required":["email","user_id","name","picture","role"],"title":"UserResponse","description":"Response schema for user info."},"UserStats":{"properties":{"user":{"$ref":"#/components/schemas/UserStatsUser"},"memories":{"additionalProperties":true,"type":"object","title":"Memories"},"api_usage":{"additionalProperties":true,"type":"object","title":"Api Usage"}},"type":"object","required":["user","memories","api_usage"],"title":"UserStats","description":"Detailed statistics for a specific user."},"UserStatsUser":{"properties":{"user_id":{"type":"string","title":"User Id"},"email":{"type":"string","title":"Email"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"role":{"type":"string","title":"Role"}},"type":"object","required":["user_id","email","name","role"],"title":"UserStatsUser","description":"The account a ``UserStats`` response describes.\n\nIssue #1882: typed so the OpenAPI public-id guard covers it, and carries\nthe string ``user_id`` instead of the integer ``users`` PK (#1813)."},"UserWithAdminFlag":{"properties":{"email":{"title":"Email"},"user_id":{"title":"User Id"},"name":{"title":"Name"},"picture":{"title":"Picture"},"role":{"title":"Role"},"is_initial_admin":{"title":"Is Initial Admin"},"created_at":{"title":"Created At"},"last_login_at":{"title":"Last Login At"},"memory_count":{"title":"Memory Count"},"is_active":{"title":"Is Active"}},"type":"object","required":["email","user_id","name","picture","role","is_initial_admin","created_at","last_login_at","memory_count","is_active"],"title":"UserWithAdminFlag","description":"User model with system admin flags for admin management.\n\nIssue #166: System Admin vs Workspace Admin RBAC separation.\nIssue #1813: no integer ``id`` — address users by ``user_id``."},"UserWorkspaceInfo":{"properties":{"workspace_id":{"title":"Workspace Id"},"workspace_name":{"title":"Workspace Name"},"role":{"title":"Role"},"is_primary":{"title":"Is Primary"},"joined_at":{"title":"Joined At"},"plan_name":{"title":"Plan Name"}},"type":"object","required":["workspace_id","workspace_name","role","is_primary","joined_at","plan_name"],"title":"UserWorkspaceInfo","description":"Workspace membership info for a user.\n\nIssue #164: User Management拡張.\nIssue #276: Slug removed."},"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"},"WorkerActiveColorResponse":{"properties":{"active_color":{"type":"string","enum":["blue","green"],"title":"Active Color"},"responding_color":{"anyOf":[{"type":"string","enum":["blue","green"]},{"type":"null"}],"title":"Responding Color"}},"type":"object","required":["active_color","responding_color"],"title":"WorkerActiveColorResponse","description":"Which blue-green color is live, and which one answered (#1482)."},"WorkerAppAdminResponse":{"properties":{"platform":{"title":"Platform"},"app_key":{"title":"App Key"},"display_name":{"title":"Display Name"},"status":{"title":"Status"},"revision":{"title":"Revision"},"has_active_secret":{"title":"Has Active Secret"},"active_secret_revision":{"title":"Active Secret Revision"},"retiring_secret_revision":{"title":"Retiring Secret Revision"},"retiring_valid_until":{"title":"Retiring Valid Until"},"created_at":{"title":"Created At"},"updated_at":{"title":"Updated At"}},"type":"object","required":["platform","app_key","display_name","status","revision","has_active_secret","active_secret_revision","retiring_secret_revision","retiring_valid_until","created_at","updated_at"],"title":"WorkerAppAdminResponse"},"WorkerAppBootstrapItem":{"properties":{"app_key":{"title":"App Key"},"platform":{"title":"Platform"},"status":{"title":"Status"},"revision":{"title":"Revision"},"active":{},"retiring":{}},"type":"object","required":["app_key","platform","status","revision"],"title":"WorkerAppBootstrapItem","description":"One app identity. Secrets are present only while the app is active."},"WorkerAppBootstrapResponse":{"properties":{"revision":{"type":"string","title":"Revision"},"apps":{"items":{"$ref":"#/components/schemas/WorkerAppBootstrapItem"},"type":"array","title":"Apps"}},"type":"object","required":["revision","apps"],"title":"WorkerAppBootstrapResponse"},"WorkerAppCreateRequest":{"properties":{"platform":{"type":"string","enum":["slack","discord","teams"],"title":"Platform"},"app_key":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"App Key"},"display_name":{"type":"string","maxLength":255,"minLength":1,"title":"Display Name"},"signing_secret":{"type":"string","maxLength":512,"minLength":1,"title":"Signing Secret"}},"type":"object","required":["platform","app_key","display_name","signing_secret"],"title":"WorkerAppCreateRequest"},"WorkerAppRotateSecretRequest":{"properties":{"signing_secret":{"type":"string","maxLength":512,"minLength":1,"title":"Signing Secret"},"retiring_for_seconds":{"type":"integer","maximum":86400.0,"minimum":0.0,"title":"Retiring For Seconds","default":3600}},"type":"object","required":["signing_secret"],"title":"WorkerAppRotateSecretRequest"},"WorkerAppUpdateRequest":{"properties":{"display_name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Display Name"},"status":{"anyOf":[{"type":"string","enum":["active","disabled"]},{"type":"null"}],"title":"Status"}},"type":"object","title":"WorkerAppUpdateRequest"},"WorkerConnectorConfig":{"additionalProperties":true,"type":"object"},"WorkerRuntimeBufferConfig":{"properties":{"ttl_seconds":{"type":"integer","maximum":86400.0,"exclusiveMinimum":0.0,"title":"Ttl Seconds","default":86400},"max_len":{"type":"integer","maximum":100000.0,"exclusiveMinimum":0.0,"title":"Max Len","default":10000}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeBufferConfig","description":"Tenant-owned buffer limits; connection coordinates are intentionally absent."},"WorkerRuntimeConfig":{"properties":{"buffer":{"$ref":"#/components/schemas/WorkerRuntimeBufferConfig"},"flush":{"$ref":"#/components/schemas/WorkerRuntimeFlushConfig"},"supervisor":{"$ref":"#/components/schemas/WorkerRuntimeSupervisorConfig"},"lifecycle":{"$ref":"#/components/schemas/WorkerRuntimeLifecycleConfig"},"continuity":{"$ref":"#/components/schemas/WorkerRuntimeContinuityConfig"},"vision_enabled":{"type":"boolean","title":"Vision Enabled","default":true},"team_scope_filter_enabled":{"type":"boolean","title":"Team Scope Filter Enabled","default":false},"channel_allowlist_enabled":{"type":"boolean","title":"Channel Allowlist Enabled","default":false},"mention_answer_enabled":{"type":"boolean","title":"Mention Answer Enabled","default":false},"answer_relevance_threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Answer Relevance Threshold","default":0.35},"answer_timeout_sec":{"type":"number","maximum":300.0,"exclusiveMinimum":0.0,"title":"Answer Timeout Sec","default":8.0},"memory_link_template":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Memory Link Template"},"entity_extraction_enabled":{"type":"boolean","title":"Entity Extraction Enabled","default":false},"entity_max":{"type":"integer","maximum":50.0,"exclusiveMinimum":0.0,"title":"Entity Max","default":8}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeConfig","description":"Complete non-secret, installation-scoped worker runtime contract."},"WorkerRuntimeContinuityConfig":{"properties":{"time_window_minutes":{"type":"integer","maximum":10080.0,"minimum":0.0,"title":"Time Window Minutes","default":30},"semantic_threshold":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Semantic Threshold","default":0.6},"semantic_check_enabled":{"type":"boolean","title":"Semantic Check Enabled","default":false}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeContinuityConfig","description":"Per-connector topic-continuity thresholds."},"WorkerRuntimeFlushConfig":{"properties":{"silence_seconds":{"type":"integer","maximum":604800.0,"minimum":0.0,"title":"Silence Seconds","default":300},"volume_tokens":{"type":"integer","maximum":1000000.0,"exclusiveMinimum":0.0,"title":"Volume Tokens","default":2000},"max_tracked_topics":{"type":"integer","maximum":100000.0,"exclusiveMinimum":0.0,"title":"Max Tracked Topics","default":10000}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeFlushConfig","description":"Per-connector topic flush thresholds."},"WorkerRuntimeLifecycleConfig":{"properties":{"deletion_mode":{"type":"string","enum":["forget","redact"],"title":"Deletion Mode","default":"redact"},"redacted_summary":{"type":"string","maxLength":512,"minLength":1,"title":"Redacted Summary","default":"[redacted on user request]"},"dormant_summary":{"type":"string","maxLength":512,"minLength":1,"title":"Dormant Summary","default":"[dormant] (channel archived)"},"edited_summary":{"type":"string","maxLength":512,"minLength":1,"title":"Edited Summary","default":"[stale] (source message edited)"}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeLifecycleConfig","description":"Connector lifecycle propagation behavior."},"WorkerRuntimeSupervisorConfig":{"properties":{"tick_seconds":{"type":"number","maximum":3600.0,"minimum":0.1,"title":"Tick Seconds","default":1.0},"shutdown_flush_timeout_seconds":{"type":"number","maximum":600.0,"exclusiveMinimum":0.0,"title":"Shutdown Flush Timeout Seconds","default":25.0}},"additionalProperties":false,"type":"object","title":"WorkerRuntimeSupervisorConfig","description":"Per-connector supervisor cadence and shutdown budget."},"WorkerSigningSecret":{"properties":{"revision":{"title":"Revision"},"signing_secret":{"title":"Signing Secret"},"valid_until":{"title":"Valid Until"}},"type":"object","required":["revision","signing_secret"],"title":"WorkerSigningSecret","description":"One revision of signing-secret material for an internal worker."},"WorkspaceConnectorCreateRequest":{"properties":{"connector_type":{"type":"string","enum":["slack","discord","teams"],"title":"Connector Type","description":"Connector backend to provision"},"resource_id":{"type":"string","maxLength":255,"minLength":1,"title":"Resource Id","description":"Workspace-scoped resource slug for ingested chat events"},"display_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Display Name"},"oauth_tokens":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Oauth Tokens","description":"OAuth token bundle; stored Fernet-encrypted"},"pii_guardrail_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Pii Guardrail Config","description":"PII guardrail config for ai-worker pre-compile"},"litellm_virtual_key_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Litellm Virtual Key Id"},"virtual_key_valid_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Virtual Key Valid Until"},"quota_events_per_hour":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Quota Events Per Hour","default":1000},"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id","description":"Existing write-target context"},"auto_create_context_name":{"anyOf":[{"type":"string","maxLength":100},{"type":"null"}],"title":"Auto Create Context Name","description":"Create a fresh private context with this name"},"llm_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Llm Config","description":"BYO LLM bundle; Fernet-encrypted"},"channel_ids":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Channel Ids","description":"Ingest channel selection"},"locale":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale","description":"Worker pre-compile locale. Must map to the worker Locale contract ('en' | 'ja'); common BCP-47 forms are normalized (ja-JP → ja). See models.worker_runtime.WORKER_LOCALES (#1377)."},"external_team_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"External Team Id","description":"Platform team id (worker dispatch key)"},"app_key":{"type":"string","maxLength":64,"minLength":1,"pattern":"^[a-z0-9][a-z0-9_-]{0,63}$","title":"App Key","description":"Stable platform app identity selector","default":"default"},"slack_install_handle":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Slack Install Handle","description":"One-time handle from the Slack OAuth callback; resolved server-side to oauth_tokens + external_team_id (bot token never sent by client)"},"runtime":{"anyOf":[{"$ref":"#/components/schemas/WorkerRuntimeConfig"},{"type":"null"}],"description":"Non-secret per-connector worker controls; omitted uses worker defaults"}},"type":"object","required":["connector_type","resource_id"],"title":"WorkspaceConnectorCreateRequest","description":"Request body for provisioning an ai-worker connector."},"WorkspaceConnectorCreateResponse":{"properties":{"connector_id":{"type":"string","format":"uuid","title":"Connector Id"},"connector_type":{"type":"string","title":"Connector Type"},"app_key":{"type":"string","title":"App Key"},"resource_id":{"type":"string","title":"Resource Id"},"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"},"token_id":{"type":"string","pattern":"^rtok_[0-9A-Za-z]{22}$","title":"Token Id","description":"Public id of the connector's resource token (`rtok_` + 22 base62 characters)"},"token":{"type":"string","title":"Token","description":"Plaintext resource token; save immediately"},"kmc_api_key":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Kmc Api Key","description":"Plaintext KMC write key; shown once (registration flow only)"},"quota_events_per_hour":{"type":"integer","title":"Quota Events Per Hour"},"idempotency_key_prefix":{"type":"string","title":"Idempotency Key Prefix"}},"type":"object","required":["connector_id","connector_type","app_key","resource_id","token_id","token","quota_events_per_hour","idempotency_key_prefix"],"title":"WorkspaceConnectorCreateResponse","description":"Connector setup response. The token + KMC key are shown exactly once."},"WorkspaceConnectorRuntimeUpdateRequest":{"properties":{"runtime":{"anyOf":[{"$ref":"#/components/schemas/WorkerRuntimeConfig"},{"type":"null"}]},"expected_config_version":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Expected Config Version"}},"type":"object","required":["runtime"],"title":"WorkspaceConnectorRuntimeUpdateRequest","description":"Complete normalized replacement for tenant-owned worker controls.\n\n``runtime`` is required but nullable: an explicit ``null`` clears the\nstored block back to NULL (worker built-in defaults) so a tuned\nconnector is not a one-way door. ``expected_config_version`` is the\noptimistic-concurrency guard for the full-document replacement: pass the\nversion your snapshot came from and a concurrent change turns into a 409\ninstead of a silent revert."},"WorkspaceConnectorRuntimeUpdateResponse":{"properties":{"connector_id":{"type":"string","format":"uuid","title":"Connector Id"},"runtime":{"$ref":"#/components/schemas/WorkerRuntimeConfig"},"stored":{"type":"boolean","title":"Stored"},"config_version":{"type":"integer","title":"Config Version"}},"type":"object","required":["connector_id","runtime","stored","config_version"],"title":"WorkspaceConnectorRuntimeUpdateResponse","description":"Updated effective controls and the new opaque-source connector revision.\n\n``runtime`` is the EFFECTIVE config (worker defaults when the stored\nblock was cleared); ``stored`` distinguishes a persisted override from\nthe cleared/defaults state."},"WorkspaceConnectorSettingsUpdateRequest":{"properties":{"channel_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Channel Ids","description":"Ingest channel selection; non-empty list of channel ids, or null to clear"},"litellm_virtual_key_id":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Litellm Virtual Key Id"},"llm_config":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Llm Config","description":"Write-only BYO LLM bundle; stored Fernet-encrypted, never returned (a presence flag is). Null clears."},"locale":{"anyOf":[{"type":"string","maxLength":10},{"type":"null"}],"title":"Locale","description":"Worker pre-compile locale. Must map to the worker Locale contract ('en' | 'ja'); common BCP-47 forms are normalized (ja-JP → ja). Null clears (#1377)."},"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id","description":"Re-point the write-target context (#1428). Must be a live context in this workspace; null clears the binding (no write target)."},"expected_config_version":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Expected Config Version"}},"additionalProperties":false,"type":"object","title":"WorkspaceConnectorSettingsUpdateRequest","description":"PATCH body for connector vend settings (#1376).\n\nTrue PATCH semantics: fields absent from the request are untouched; an\nexplicit ``null`` clears. The route forwards ``exclude_unset`` fields to\nthe service sentinel, so every default below is just the \"absent\" marker.\n``extra=\"forbid\"`` because a silently-dropped typo'd field name would\nread as a successful partial update (#1376 review)."},"WorkspaceConnectorSettingsUpdateResponse":{"properties":{"connector_id":{"type":"string","format":"uuid","title":"Connector Id"},"channel_ids":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Channel Ids"},"litellm_virtual_key_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Litellm Virtual Key Id"},"llm_config_present":{"type":"boolean","title":"Llm Config Present"},"locale":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Locale"},"config_version":{"type":"integer","title":"Config Version"},"context_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Context Id"}},"type":"object","required":["connector_id","channel_ids","litellm_virtual_key_id","llm_config_present","locale","config_version","context_id"],"title":"WorkspaceConnectorSettingsUpdateResponse","description":"Post-update settings. The LLM bundle itself is never echoed."},"WorkspaceConnectorSummary":{"properties":{"connector_id":{"title":"Connector Id"},"connector_type":{"title":"Connector Type"},"app_key":{"title":"App Key"},"resource_id":{"title":"Resource Id"},"context_id":{"title":"Context Id"},"config_version":{"title":"Config Version"},"created_at":{"title":"Created At"},"created_by":{"title":"Created By"},"runtime":{},"channel_ids":{"title":"Channel Ids"},"locale":{"title":"Locale"},"litellm_virtual_key_id":{"title":"Litellm Virtual Key Id"},"llm_config_present":{"title":"Llm Config Present"},"display_name":{"title":"Display Name"},"external_team_id":{"title":"External Team Id"},"context_name":{"title":"Context Name"},"last_memory_at":{"title":"Last Memory At"},"memories_last_7d":{"title":"Memories Last 7D"},"ingest_context_shared":{"title":"Ingest Context Shared"}},"type":"object","required":["connector_id","connector_type","app_key","resource_id","config_version","created_at"],"title":"WorkspaceConnectorSummary","description":"One connector row for the workspace list view."},"WorkspaceContextStatsResponse":{"properties":{"contexts":{"items":{"$ref":"#/components/schemas/ContextStatsItem"},"type":"array","title":"Contexts"},"total_contexts":{"type":"integer","title":"Total Contexts"},"workspace_totals":{"$ref":"#/components/schemas/WorkspaceTotals"}},"type":"object","required":["contexts","total_contexts","workspace_totals"],"title":"WorkspaceContextStatsResponse","description":"Response model for context statistics.\n\nIssue #249: GET /api/v1/workspaces/{workspace_id}/contexts/stats"},"WorkspaceCreate":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"openai_api_key":{"anyOf":[{"type":"string","minLength":20,"pattern":"^sk-[A-Za-z0-9_-]+$"},{"type":"null"}],"title":"Openai Api Key"},"description":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Description"},"default_context_name":{"anyOf":[{"type":"string","maxLength":100,"pattern":"^[a-z0-9_-]+$"},{"type":"null"}],"title":"Default Context Name"},"default_context_summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Default Context Summary"},"default_context_usage_guide":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Default Context Usage Guide"},"default_context_embedding_model":{"anyOf":[{"type":"string","pattern":"^(text\\-embedding\\-3\\-small|text\\-embedding\\-3\\-large|qwen3\\-embedding:0\\.6b|qwen3\\-embedding:4b|qwen3\\-embedding:8b|nomic\\-embed\\-text|mxbai\\-embed\\-large)$"},{"type":"null"}],"title":"Default Context Embedding Model"}},"type":"object","required":["name"],"title":"WorkspaceCreate","description":"Request model for creating workspace.\n\nIssue #146: OpenAI API key is now optional (can be added later).\nIssue #169: Default context settings (summary, usage_guide, embedding_model).\nIssue #276: Slug removed - not used for routing, simplified UX."},"WorkspaceEntitlementView":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"plan_name":{"type":"string","title":"Plan Name"},"addons":{"additionalProperties":{"type":"integer"},"type":"object","title":"Addons"},"entitlement_source":{"type":"string","title":"Entitlement Source"}},"type":"object","required":["workspace_id","plan_name","addons","entitlement_source"],"title":"WorkspaceEntitlementView","description":"Read model for the reconciler (#1095): the current entitlement + provenance."},"WorkspaceInvitationCreate":{"properties":{"email":{"type":"string","maxLength":255,"pattern":"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$","title":"Email","description":"Email address (must match Google OAuth account)"},"role":{"$ref":"#/components/schemas/WorkspaceRole","description":"Role to assign upon acceptance (owner is not invitable — #1166)","default":"member"},"expires_in_days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days","description":"Days until expiration (7/30/90/365 or null=never)"},"allowed_context_ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array"},{"type":"null"}],"title":"Allowed Context Ids","description":"Context IDs to allow (required for member/viewer, minimum 1)"}},"type":"object","required":["email"],"title":"WorkspaceInvitationCreate","description":"Create workspace invitation request.\n\nIssue #165: Team Collaboration - Workspace Invitation System\n\nNote: Email is required and must match the Google account used for OAuth login."},"WorkspaceInvitationResponse":{"properties":{"id":{"title":"Id"},"workspace_id":{"title":"Workspace Id"},"token":{"title":"Token"},"email":{"title":"Email"},"role":{"title":"Role"},"invited_by":{"title":"Invited By"},"expires_at":{"title":"Expires At"},"accepted_at":{"title":"Accepted At"},"accepted_by":{"title":"Accepted By"},"created_at":{"title":"Created At"},"invitation_url":{"title":"Invitation Url"},"is_expired":{"title":"Is Expired"},"is_accepted":{"title":"Is Accepted"},"allowed_context_ids":{"title":"Allowed Context Ids","description":"Allowed context IDs for member/viewer roles (Migration 042)"}},"type":"object","required":["id","workspace_id","token","email","role","invited_by","expires_at","accepted_at","accepted_by","created_at","invitation_url","is_expired","is_accepted"],"title":"WorkspaceInvitationResponse","description":"Workspace invitation response.\n\nIssue #165: Team Collaboration - Workspace Invitation System"},"WorkspaceMemberResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"user_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Name"},"user_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Email"},"role":{"type":"string","title":"Role"},"joined_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Joined At"},"seat_paused":{"type":"boolean","title":"Seat Paused","default":false},"credentials_status":{"anyOf":[{"$ref":"#/components/schemas/CredentialsStatusInfo"},{"type":"null"}]},"last_login_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Login At"},"allowed_context_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Allowed Context Ids"}},"type":"object","required":["user_id","role","joined_at"],"title":"WorkspaceMemberResponse","description":"Response model for workspace member."},"WorkspaceOwnerView":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"sub":{"type":"string","title":"Sub"},"is_owner":{"type":"boolean","title":"Is Owner"}},"type":"object","required":["workspace_id","sub","is_owner"],"title":"WorkspaceOwnerView","description":"Answer of the ownership re-check: is ``sub`` the workspace owner right now."},"WorkspacePlanInfo":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"workspace_name":{"type":"string","title":"Workspace Name"},"current_plan":{"type":"string","title":"Current Plan"},"plan_display_name":{"type":"string","title":"Plan Display Name"},"usage":{"additionalProperties":true,"type":"object","title":"Usage"},"quotas":{"$ref":"#/components/schemas/WorkspacePlanQuotas"},"can_upgrade":{"type":"boolean","title":"Can Upgrade"},"can_downgrade":{"type":"boolean","title":"Can Downgrade"},"suspended":{"items":{"type":"string"},"type":"array","title":"Suspended"}},"type":"object","required":["workspace_id","workspace_name","current_plan","plan_display_name","usage","quotas","can_upgrade","can_downgrade"],"title":"WorkspacePlanInfo","description":"Workspace plan information with usage stats."},"WorkspacePlanQuotas":{"properties":{"memory_limit":{"type":"integer","title":"Memory Limit"},"max_contexts":{"type":"integer","title":"Max Contexts"},"max_resource_tokens":{"type":"integer","title":"Max Resource Tokens"},"max_quota_capacity":{"type":"integer","title":"Max Quota Capacity"},"mcp_calls_per_day":{"type":"integer","title":"Mcp Calls Per Day"},"mcp_calls_per_week":{"type":"integer","title":"Mcp Calls Per Week"},"rest_calls_per_day":{"type":"integer","title":"Rest Calls Per Day"},"public_calls_per_day":{"type":"integer","title":"Public Calls Per Day"}},"type":"object","required":["memory_limit","max_contexts","max_resource_tokens","max_quota_capacity","mcp_calls_per_day","mcp_calls_per_week","rest_calls_per_day","public_calls_per_day"],"title":"WorkspacePlanQuotas","description":"Effective quotas of one workspace, as ``GET /workspaces/{id}/plan`` returns them.\n\nTyped rather than a bare ``dict`` because the frontend reads named keys off\nthis payload: the token screens take \"used / max\" and the quota-capacity\nline from ``max_resource_tokens`` / ``max_quota_capacity`` (#1560). A rename\nhere must fail pyright at the construction site, not silently degrade the UI\nto its \"cap unknown\" fallback."},"WorkspaceQuotaDetail":{"properties":{"workspace_id":{"type":"string","title":"Workspace Id"},"workspace_name":{"type":"string","title":"Workspace Name"},"plan_name":{"type":"string","title":"Plan Name"},"base":{"$ref":"#/components/schemas/QuotaBreakdown"},"addon":{"$ref":"#/components/schemas/AddonValues"},"effective":{"$ref":"#/components/schemas/QuotaBreakdown"},"usage":{"$ref":"#/components/schemas/UsageValues"},"spend_cap":{"anyOf":[{"$ref":"#/components/schemas/SpendCapValues"},{"type":"null"}]}},"type":"object","required":["workspace_id","workspace_name","plan_name","base","addon","effective","usage"],"title":"WorkspaceQuotaDetail","description":"Detailed quota breakdown for a workspace."},"WorkspaceResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"owner_user_id":{"type":"string","title":"Owner User Id"},"plan_name":{"type":"string","title":"Plan Name"},"member_count":{"type":"integer","title":"Member Count"},"context_count":{"type":"integer","title":"Context Count"},"max_contexts":{"type":"integer","title":"Max Contexts"},"created_at":{"type":"string","title":"Created At"},"current_user_role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Current User Role"},"analyses_enabled":{"type":"boolean","title":"Analyses Enabled","default":false}},"type":"object","required":["id","name","description","owner_user_id","plan_name","member_count","context_count","max_contexts","created_at"],"title":"WorkspaceResponse","description":"Response model for workspace.\n\nIssue #276: Slug removed."},"WorkspaceRole":{"type":"string","enum":["owner","admin","member","viewer"],"title":"WorkspaceRole","description":"Workspace-level tenancy role.\n\nOrdering: OWNER > ADMIN > MEMBER > VIEWER (see WORKSPACE_ROLE_WEIGHTS)."},"WorkspaceStatsResponse":{"properties":{"total_memories":{"type":"integer","title":"Total Memories"},"context_count":{"type":"integer","title":"Context Count"},"contexts":{"items":{"$ref":"#/components/schemas/ContextStats"},"type":"array","title":"Contexts"},"private_aggregation":{"anyOf":[{"$ref":"#/components/schemas/PrivateContextAggregation"},{"type":"null"}]},"plan_name":{"type":"string","title":"Plan Name"},"unsearchable_memories":{"type":"integer","title":"Unsearchable Memories","default":0},"stalled_memories":{"type":"integer","title":"Stalled Memories","default":0}},"type":"object","required":["total_memories","context_count","contexts","plan_name"],"title":"WorkspaceStatsResponse","description":"Aggregated statistics across all user's contexts.\n\nIssue #165: Privacy-aware response with aggregation for inaccessible contexts.\nSingle Collection Migration: total_storage_mb removed (memory count only)."},"WorkspaceTotals":{"properties":{"memory_count":{"type":"integer","title":"Memory Count"}},"type":"object","required":["memory_count"],"title":"WorkspaceTotals","description":"Workspace-wide totals.\n\nIssue #249: Summary totals for all contexts."},"WorkspaceUpdate":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","title":"WorkspaceUpdate","description":"Request model for updating workspace.\n\nIssue #276: Slug removed."},"WorkspacesUsage":{"properties":{"used":{"type":"integer","title":"Used","description":"Owned workspaces (deleted_at IS NULL)","default":0},"limit":{"type":"integer","title":"Limit","description":"Effective cap: 1 (base) + users.workspace_slot_bonus (#675)","default":0},"remaining":{"type":"integer","title":"Remaining","description":"max(0, limit - used)","default":0}},"type":"object","title":"WorkspacesUsage","description":"Owned-workspace cap usage (Issue #661).\n\nUser-level — the cap is per-user, not per-workspace. The dashboard\nsurfaces this so users can see \"X / N workspaces owned\" before\nhitting the cap on workspace creation.\n\nUnlike ``AnalysisUsage`` / ``SleepContextsUsage``, this field is\npopulated regardless of which workspace the caller currently has\nselected — it is the user's own quota across all their owned\nworkspaces.\n\nNo ``addon_bonus`` field as of Issue #661 because there is no\nper-user addon SKU. If ``addon_workspace_bonus`` (or equivalent)\nis introduced — see the Out-of-scope section of #661 — add\n``addon_bonus`` here to match ``SleepContextsUsage`` /\n``AnalysisUsage`` shape."},"WriteLintHint":{"properties":{"code":{"type":"string","title":"Code","description":"Stable machine-readable hint id: summary_short, summary_long, summary_narrative, no_tags, tag_near_duplicate, tag_normalized."},"hint":{"type":"string","title":"Hint","description":"What to do differently, in one sentence."},"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subject","description":"The specific value the hint is about (e.g. the tag), when applicable."},"replacement":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Replacement","description":"tag_normalized only: the stored spelling that replaced ``subject`` (remember(tags_normalize=true), #1853)."}},"type":"object","required":["code","hint"],"title":"WriteLintHint","description":"One advisory recall-ability hint about a just-written memory.\n\nAdvisory only: the memory is already stored, and hints never affect the\nwrite. They exist because recall quality depends on how the summary and\ntags were written, and nothing else tells the writer at the time.\n\n``tag_near_duplicate`` compares against a per-context tag vocabulary that\nthe server caches for up to two minutes (#1512). Tags written through this\nAPI are reflected immediately; a tag removed by forget() or an update may\nstill be cited for up to that window."}}},"tags":[{"name":"authentication","description":"OAuth2 login and session management"},{"name":"google-oauth2","description":"Google OAuth2 login and callback flow"},{"name":"github-oauth2","description":"GitHub OAuth2 login and callback flow"},{"name":"users","description":"User profile and settings"},{"name":"account-erasure","description":"GDPR Art.17 / APPI 第22条 right-to-erasure self-service flow (Issue #360). Session-only auth (no API keys)."},{"name":"account-linking","description":"Sign-in providers: attach, detach and list the external login providers (Google, GitHub) of the current account — several ways to sign in to ONE account. Not to be confused with identity-links."},{"name":"identity-links","description":"Identity links (Issue #1784): count several accounts of one person — each with its own sign-in — as one owner of their private contexts. Roles and workspace membership stay per account. Browser session only."},{"name":"me-oauth","description":"Refresh the current user's OAuth-backed session credentials"},{"name":"me-terms","description":"Terms-of-service re-acceptance (Issue #1665): the signed-in user accepts the current TERMS_VERSION. 404s unless the deployment sets it."},{"name":"account-password","description":"Set up, change or remove the signed-in account's password (Issue #1678). Session-only auth (no API keys)."},{"name":"workspace","description":"Workspace dashboard and stats"},{"name":"workspaces","description":"Workspace CRUD operations"},{"name":"workspace-plan","description":"Workspace plan management (self-service)"},{"name":"invitations","description":"Team member invitations"},{"name":"member-credentials","description":"API key and OAuth credential management"},{"name":"agents","description":"Agent Control Plane preview: workspace Agent Registry, subtractive context bindings, and composed session bootstrap"},{"name":"billing-handoff","description":"Create a signed handoff from Memory Cloud to the external billing service"},{"name":"contexts","description":"Context management"},{"name":"context-search-config","description":"Per-context search settings"},{"name":"memory","description":"MCP memory operations (remember, recall, forget, explore, reference)"},{"name":"attachments","description":"DEPRECATED (Issue #555): legacy file attachment routes return HTTP 410 Gone. Use the `files` tag (`/api/v1/files/*`) instead."},{"name":"files","description":"Platform-managed file storage (Issue #485). Presigned upload to Cloudflare R2, per-workspace quota."},{"name":"graph","description":"Neural Memory graph operations"},{"name":"agent-state","description":"Agent Memory Substrate (epic #885): per-context key→value scratch state lane with optional TTL. Structurally excluded from recall(). REST companion to the set_state / get_state MCP tools."},{"name":"retrieval-feedback","description":"Agent Memory Substrate (epic #885): append-only retrieval-helpful signal per (context, memory). Read-adjacent (Viewer+); collected but not auto-acted-on until the golden eval gate (#344) is green. REST companion to the feedback MCP tool."},{"name":"api-keys","description":"MCP API key management"},{"name":"share-keys","description":"Share-key lifecycle, public recall, and share-session inspection"},{"name":"oauth2-server","description":"OAuth2 client management"},{"name":"external-keys","description":"External API keys (OpenAI, Cohere, etc.)"},{"name":"secrets","description":"Zero-knowledge secret store (age ciphertext; server never decrypts)"},{"name":"resources","description":"Resource listing and event-history inspection"},{"name":"resource-tokens","description":"Resource token management"},{"name":"resource-ingest","description":"External data ingestion API"},{"name":"resource-schema","description":"Resource schema registry"},{"name":"resource-indexer","description":"Resource indexer runtime state"},{"name":"workspace-connectors","description":"ai-worker chat-ingest connector provisioning"},{"name":"connectors","description":"External connector installation and callback flows (currently Slack)"},{"name":"workers","description":"Authenticated connector-worker runtime configuration and deployment discovery"},{"name":"analyses","description":"Memory Analysis runs. REST companion to the 5 MCP tools (analyze_context / get_analysis / list_analyses / get_active_analysis / get_cluster). Behind a 4-stage gate: workspace owner + Pro tier + per-day quota + workspace allowlist."},{"name":"referrals","description":"Referral program (Issue #1470): the caller's own referral code and standing, plus code redemption. Session auth only, and the whole surface 404s unless the deployment sets ENABLE_REFERRALS."},{"name":"beta-invites","description":"Closed-beta invite links (Issue #1581): a signed-in user mints a one-time link that lets one new person through the signup gate. Session auth only except the public preview, and the whole surface 404s unless the deployment sets ENABLE_BETA_INVITES."},{"name":"public-search","description":"Public REST API for search"},{"name":"mcp-api","description":"MCP server tools listing and status"},{"name":"admin","description":"Admin: user management and system stats"},{"name":"admin-plans","description":"Admin: plan tier and quota management"},{"name":"admin-worker-apps","description":"Admin: worker app identity and signing-secret lifecycle"},{"name":"admin-memory-health","description":"Admin: per-context memory health diagnosis and signal breakdown"},{"name":"admin-neural","description":"Admin: trigger Neural Memory recalibration"},{"name":"admin-referrals","description":"Admin: referral grant ledger and per-grant revocation"},{"name":"admin-signup-gate","description":"Admin: signup-gate configuration and allowlist management"},{"name":"admin-sleep","description":"Admin: manually run Sleep Maintenance and undo individual merges"},{"name":"neural-config","description":"Admin: neural memory configuration"},{"name":"sleep-reports","description":"Admin: Sleep Maintenance execution reports and audit log"},{"name":"bm25-drift","description":"Admin: BM25 IDF drift observability (Issue #343). Cron is disabled by default and scheduled for production enablement in v0.14.0 alongside Privacy Policy (#359) and right-to-erasure (#360)."},{"name":"system-admins","description":"Admin: system administrator management"},{"name":"internal-billing","description":"Internal service-authenticated plan and downgrade-eligibility endpoints"},{"name":"config","description":"Environment configuration management (admin)"},{"name":"system","description":"System health and info"},{"name":"well-known","description":"OAuth2 discovery endpoints"}]}