{"openapi":"3.1.0","info":{"title":"Quelvio Enterprise API","description":"Enterprise Knowledge Intelligence Platform","version":"v1.4.20"},"paths":{"/v1/health":{"get":{"tags":["Health"],"summary":"Health Check","description":"ALB probe: process liveness plus bounded DB authentication.\n\nThe DB check uses a separate ``NullPool`` engine, so it proves that the\ntask's currently injected password can authenticate a *new* connection\nafter RDS secret rotation. It does not use the shared application pool and\nonly runs ``SELECT 1`` under a hard timeout. Results are cached for one ALB\ninterval to avoid public health traffic creating DB pressure.","operationId":"health_check_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/healthz":{"get":{"tags":["Health"],"summary":"Healthz","description":"Detailed health probe for ECS + external monitoring.\n\nRuns all dependency checks in parallel. Returns 200 when every check\nis ``ok``, 503 otherwise. Total response time stays under ~500ms even\nwhen a dependency is timing out.","operationId":"healthz_v1_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Healthz V1 Healthz Get"}}}}}}},"/health":{"get":{"tags":["Health Root"],"summary":"Health Check","description":"ALB probe: process liveness plus bounded DB authentication.\n\nThe DB check uses a separate ``NullPool`` engine, so it proves that the\ntask's currently injected password can authenticate a *new* connection\nafter RDS secret rotation. It does not use the shared application pool and\nonly runs ``SELECT 1`` under a hard timeout. Results are cached for one ALB\ninterval to avoid public health traffic creating DB pressure.","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/healthz":{"get":{"tags":["Health Root"],"summary":"Healthz","description":"Detailed health probe for ECS + external monitoring.\n\nRuns all dependency checks in parallel. Returns 200 when every check\nis ``ok``, 503 otherwise. Total response time stays under ~500ms even\nwhen a dependency is timing out.","operationId":"healthz_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Healthz Healthz Get"}}}}}}},"/v1/admin/dashboard/overview":{"get":{"tags":["Admin"],"summary":"Get Overview","description":"Combined overview: metrics + Quelvio Score + nightly jobs.","operationId":"get_overview_v1_admin_dashboard_overview_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverviewResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/dashboard/enterprise-snapshot":{"get":{"tags":["Admin"],"summary":"Get Enterprise Snapshot","description":"Brain Overview snapshot: global totals + per-tenant rows.\n\nPowers the backoffice Brain Overview tab. See\n``GetEnterpriseSnapshotQuery`` for the per-row computation contract\n(especially the MRR precedence ladder).","operationId":"get_enterprise_snapshot_v1_admin_dashboard_enterprise_snapshot_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnterpriseSnapshotResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/content":{"get":{"tags":["Admin"],"summary":"Search Content","description":"Search content pieces by various criteria.","operationId":"search_content_v1_admin_content_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Search title","title":"Q"},"description":"Search title"},{"name":"author","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Author name or email","title":"Author"},"description":"Author name or email"},{"name":"source_url","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"}},{"name":"content_hash","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Hash"}},{"name":"enrichment_stage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Enrichment Stage"}},{"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/ContentSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/content/{content_id}":{"get":{"tags":["Admin"],"summary":"Get Content Detail","description":"Full content piece detail for drill-down.","operationId":"get_content_detail_v1_admin_content__content_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"content_id","in":"path","required":true,"schema":{"type":"string","title":"Content Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/content/{content_id}/withdraw":{"post":{"tags":["Admin"],"summary":"Withdraw Content","description":"Withdraw content on behalf of an author or content owner.","operationId":"withdraw_content_v1_admin_content__content_id__withdraw_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"content_id","in":"path","required":true,"schema":{"type":"string","title":"Content Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WithdrawContentRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/content/{content_id}/retry":{"post":{"tags":["Admin"],"summary":"Retry Enrichment","description":"Re-queue a single failed content piece for enrichment.","operationId":"retry_enrichment_v1_admin_content__content_id__retry_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"content_id","in":"path","required":true,"schema":{"type":"string","title":"Content Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/content/bulk-retry":{"post":{"tags":["Admin"],"summary":"Bulk Retry Enrichment","description":"Re-queue multiple failed content pieces for enrichment.","operationId":"bulk_retry_enrichment_v1_admin_content_bulk_retry_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkRetryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/content/{content_id}/reset-enrichment":{"post":{"tags":["Admin"],"summary":"Reset Enrichment","description":"Reset a content piece to the post-ingestion stage and delete its chunks.\n\nReads from ``EnterpriseContentPiece``. The marketplace bridge\nfallback was dropped in Commit 4d alongside the destructive\nmigration.","operationId":"reset_enrichment_v1_admin_content__content_id__reset_enrichment_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"content_id","in":"path","required":true,"schema":{"type":"string","title":"Content Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Reset Enrichment V1 Admin Content  Content Id  Reset Enrichment Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/respond":{"post":{"tags":["Admin"],"summary":"Submit Survey","description":"Submit a Quelvio Score survey response.","operationId":"submit_survey_v1_admin_surveys_respond_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyResponseRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminActionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/ranking-config/{tenant_id}":{"get":{"tags":["Admin"],"summary":"List Ranking Config","description":"Return every tenant_ranking_config row for one tenant.\n\nRead-only — no audit log. Used by the backoffice UI to render the\n\"Ranking capabilities\" panel for a specific tenant and power the\nPATCH flow below (the admin picks a capability to override from\nthis list).","operationId":"list_ranking_config_v1_admin_ranking_config__tenant_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/RankingConfigRowResponse"},"title":"Response List Ranking Config V1 Admin Ranking Config  Tenant Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/ranking-config/{tenant_id}/{capability}":{"patch":{"tags":["Admin"],"summary":"Override Ranking Config","description":"Manually override one (tenant, capability) ranking config row.\n\nTwo modes:\n  * ``status`` set (and ``clear_override`` False) → force the row\n    to the requested state AND set ``manually_overridden=true``.\n    The nightly evaluator will skip this row until the override is\n    cleared.\n  * ``clear_override=true`` (and ``status`` None) → clear the manual\n    override flag; the evaluator resumes governance on the next run.\n\nSetting both is rejected (ambiguous intent). Setting neither is\nrejected (no-op request).\n\nThe current row state is audited under ``action='ranking_config.\noverride'`` (or ``.clear_override'``) with the admin's email as the\nactor and the supplied ``reason`` preserved in metadata.\n\nReturns the row in its post-override shape so the backoffice UI\ncan reflect the change without a separate GET.","operationId":"override_ranking_config_v1_admin_ranking_config__tenant_id___capability__patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"capability","in":"path","required":true,"schema":{"type":"string","title":"Capability"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RankingConfigOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RankingConfigRowResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/orchestrator-metrics":{"get":{"tags":["Admin"],"summary":"Get Survey Orchestrator Metrics","description":"Per-instrument response rate + dismissal rate + score average\nover a rolling window. The self-tuning surface — falling response\nrate is a signal to retune cooldowns / triggers.","operationId":"get_survey_orchestrator_metrics_v1_admin_surveys_orchestrator_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyOrchestratorMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/comments":{"get":{"tags":["Admin"],"summary":"List Survey Comments","description":"Qualitative-review surface — comments alongside scores. A 7 with\na comment beats a 9 without one. Default ``has_comment_only=True``\nbiases the surface toward the rows operators actually need to read.","operationId":"list_survey_comments_v1_admin_surveys_comments_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"instrument","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Instrument"}},{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}},{"name":"billing_state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Billing State"}},{"name":"has_comment_only","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Has Comment Only"}},{"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"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/SurveyResponseWithCommentRowResponse"},"title":"Response List Survey Comments V1 Admin Surveys Comments Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/pmf/metrics":{"get":{"tags":["Admin"],"summary":"Get Pmf Metrics","description":"PMF Index + per-answer distribution, overall and segmented by\nbilling_state. The headline metric the operator uses to retune\nPMF triggers + cooldowns post-launch.","operationId":"get_pmf_metrics_v1_admin_surveys_pmf_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PMFMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/instruments/{instrument}/enabled":{"get":{"tags":["Admin"],"summary":"Get Instrument Enabled","description":"Current value of ``surveys.{instrument}.enabled`` — the\norchestrator's kill-switch read. Operators check this before\nflipping; the backoffice toggle calls this to display state.","operationId":"get_instrument_enabled_v1_admin_surveys_instruments__instrument__enabled_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"instrument","in":"path","required":true,"schema":{"type":"string","title":"Instrument"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InstrumentEnabledStateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Admin"],"summary":"Set Instrument Enabled","description":"Flip ``surveys.{instrument}.enabled``. The orchestrator reads\nthis on every next_eligible call (no cache), so flipping false\nstops new prompts for that instrument within seconds — the\ndispatch's kill-switch contract, no redeploy.\n\nAlready-rendered surveys finish normally; only future\nnext_eligible decisions are affected.","operationId":"set_instrument_enabled_v1_admin_surveys_instruments__instrument__enabled_patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"instrument","in":"path","required":true,"schema":{"type":"string","title":"Instrument"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetInstrumentEnabledRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetInstrumentEnabledResponseSchema"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/nps/metrics":{"get":{"tags":["Admin"],"summary":"Get Nps Metrics","description":"NPS raw mean + NPS Index + 11-bar histogram, overall and\nsegmented by billing_state. The dual headline matches the\ndispatch's §C.2 rule — never average raw mean and NPS Index.","operationId":"get_nps_metrics_v1_admin_surveys_nps_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NPSMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/csat/metrics":{"get":{"tags":["Admin"],"summary":"Get Csat Metrics","description":"CSAT per-feature cards (mean, response_count, dismissal_count,\nresponse_rate) + per-billing_state breakdown per feature. Features\nsorted by response volume desc — the operator sees high-traffic\nfeatures first.","operationId":"get_csat_metrics_v1_admin_surveys_csat_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CSATMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/surveys/ces/metrics":{"get":{"tags":["Admin"],"summary":"Get Ces Metrics","description":"CES per-task cards. Tasks sorted by mean score ASC — the\noperator sees the highest-friction tasks first. Dismissal count\nis prominent (CES toasts auto-dismiss at 15s → dismissal-rate is\na high-signal cooldown-tuning metric).","operationId":"get_ces_metrics_v1_admin_surveys_ces_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CESMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/permission-backfill/summary":{"get":{"tags":["Admin"],"summary":"Get Permission Backfill Summary","description":"Headline counts for the backfill dashboard.\n\nAggregates every Phase 4 ``permission_backfill_progress`` row\n(``run_id IS NULL``) into bucketed counts: completed, errored,\nrunning (advanced in last 24h), killed (stalled before the 24h\ncutoff). Phase 3C bulk-PATCH rows are excluded — they have their\nown polling endpoint.","operationId":"get_permission_backfill_summary_v1_admin_permission_backfill_summary_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionBackfillSummaryResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/permission-backfill/tenant/{tenant_id}":{"get":{"tags":["Admin"],"summary":"Get Permission Backfill Tenant Detail","description":"Per-tenant backfill progress + connector breakdown.\n\nReturns the tenant's Phase 4 progress row joined with the tenant\nname, plus a per-connector aggregation read from the\n``enterprise_content_pieces`` data plane (backfilled vs pending).\nThe data-plane read means operators see the truth even if a\nrunner-crash left the progress row counters stale.\n\nRaises 400 on a malformed tenant_id and 404 when the tenant\ndoesn't exist OR has no Phase 4 progress row.","operationId":"get_permission_backfill_tenant_detail_v1_admin_permission_backfill_tenant__tenant_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionBackfillTenantDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/permission-backfill/audit-tail":{"get":{"tags":["Admin"],"summary":"Get Permission Backfill Audit Tail","description":"Most-recent ``permission_audit_log`` rows.\n\nOrdered ``mutated_at DESC`` so the live tail rendering picks up the\nnewest mutations first. Powers the audit-strip on the operator\ndashboard.","operationId":"get_permission_backfill_audit_tail_v1_admin_permission_backfill_audit_tail_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 lower bound on ``mutated_at`` (inclusive). Omit for no bound — caller sees the most-recent ``limit`` rows.","title":"Since"},"description":"ISO 8601 lower bound on ``mutated_at`` (inclusive). Omit for no bound — caller sees the most-recent ``limit`` rows."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max rows returned. Default 100, max 500 (protects Postgres — larger exports should hit the table directly via DBA tools).","default":100,"title":"Limit"},"description":"Max rows returned. Default 100, max 500 (protects Postgres — larger exports should hit the table directly via DBA tools)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionAuditTailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/permission-backfill/tenant/{tenant_id}/trigger":{"post":{"tags":["Admin"],"summary":"Trigger Permission Backfill For Tenant","description":"Manually trigger the permission-backfill runner for one tenant.\n\nSprint 3 canary kickoff path. The nightly orchestrator already walks\nevery tenant once a day — this endpoint just exposes the same\nper-tenant runner call to an operator who needs to target one\ntenant on-demand (typical use: kicking a small canary tenant after\na runner bugfix lands, before the next 02:25 UTC nightly tick).\n\nThe runner is the existing\n:class:`PermissionBackfillRunner.run_for_tenant`; this endpoint\nadds nothing to the per-piece resolution path. The new surface is\naudit + idempotency:\n\n  * One ``permission_audit_log`` row is written for the kickoff\n    itself with ``mutation_source='manual_override'`` (the closest\n    existing closed-enum value; a future migration may add a\n    dedicated ``backfill_manual_kickoff`` source). ``actor_kind=\n    'operator'``, ``actor_email`` resolved from the admin identity,\n    ``request_id`` pulled from ``RequestContextMiddleware``.\n  * 409 returned when the tenant's progress row is currently\n    ``in_progress`` (``started_at`` set AND ``completed_at`` NULL\n    AND ``last_error`` NULL) — operator must either wait or pass\n    ``force=True`` (which clears ``completed_at`` first, see below).\n  * ``force=True`` lets the operator re-run a tenant whose row is\n    already ``completed_at IS NOT NULL`` — rare, used when a runner\n    bugfix requires a full re-walk under the new binary.\n\nOperator note: this endpoint does NOT toggle\n``Settings.permission_backfill_enabled`` — the kill-switch remains\nthe only mechanism for emergency disable. A tenant whose nightly\nhas been killed is RE-RUNNABLE here regardless of the kill-switch\nstate (the runner's per-batch kill-switch check still applies and\nwill short-circuit if the switch is engaged at runtime).","operationId":"trigger_permission_backfill_for_tenant_v1_admin_permission_backfill_tenant__tenant_id__trigger_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/TenantBackfillTriggerRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantBackfillTriggerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/permission-audit-log/stream":{"get":{"tags":["Admin · Permission Audit Live Tail"],"summary":"SSE live-tail of permission_audit_log rows","description":"Open an SSE stream that tails ``permission_audit_log``.\n\nBehaviour:\n    * Emits the last 10 audit-log rows on connect as ``event:\n      audit_log_row`` events (newest first).\n    * Emits a single ``event: backfill_complete`` once initial\n      backfill is done.\n    * Polls every ~2s thereafter; new rows are emitted oldest-first\n      as additional ``event: audit_log_row`` events.\n    * Sends a ``:heartbeat`` SSE comment every ~30s to keep\n      intermediaries from closing the connection.\n    * Closes cleanly on client disconnect (no DB resources leak).\n\nHeaders ``Cache-Control: no-cache, no-store`` + ``X-Accel-Buffering:\nno`` mirror the enterprise streaming route — they tell ALB /\nCloudflare / nginx not to buffer the response. Without those, the\nproxy holds bytes until a full buffer flushes and the operator\nsees row bursts every ~30s instead of the intended 2s cadence.","operationId":"stream_permission_audit_log_v1_admin_permission_audit_log_stream_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"If present, scope the stream (initial backfill + live polling) to this tenant. UUID, exact match.","title":"Tenant Id"},"description":"If present, scope the stream (initial backfill + live polling) to this tenant. UUID, exact match."},{"name":"mutation_source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"If present, scope the stream to a single ``mutation_source`` value (e.g. ``manual_override``, ``connector_sync``). Exact match against the CHECK-constrained closed enum.","title":"Mutation Source"},"description":"If present, scope the stream to a single ``mutation_source`` value (e.g. ``manual_override``, ``connector_sync``). Exact match against the CHECK-constrained closed enum."}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/cancel-check":{"get":{"tags":["Admin · Tenant Billing"],"summary":"Get Cancel Check","description":"Return the full 9-blocker WS-4.4a verdict for a tenant.\n\nRead-only — no audit log entry. Operators use this to triage\n\"why can't this tenant cancel?\" before forcing state changes.\nThe response includes ALL 9 blockers (not just tripped ones) so\nSTUB classifications surface alongside REAL verdicts and Phase B\ncan grep response payloads to verify replacement work.","operationId":"get_cancel_check_v1_admin_tenants__tenant_id__cancel_check_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelCheckResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/audit-log":{"get":{"tags":["Admin · Tenant Billing"],"summary":"Get Tenant Audit Log","description":"Return the merged tenant audit history, newest first.\n\nComposes ``tenant_status_history`` (state transitions), ``admin_\naudit_log`` (operator actions), and ``processed_events`` (Stripe\nwebhook deliveries linked via ``metadata.tenant_id``). Each source\nis queried with ``LIMIT limit+1`` so the merge can detect whether a\nfurther page exists; the response carries ``next_cursor`` set to\nthe oldest returned occurred_at when more data follows.\n\nRead-only — no audit log entry written for the read itself.","operationId":"get_tenant_audit_log_v1_admin_tenants__tenant_id__audit_log_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"before","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"ISO 8601 cursor — return entries with occurred_at strictly less than this value. Use the previous response's ``next_cursor`` for subsequent pages.","title":"Before"},"description":"ISO 8601 cursor — return entries with occurred_at strictly less than this value. Use the previous response's ``next_cursor`` for subsequent pages."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuditLogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/issue-credit":{"post":{"tags":["Admin · Tenant Billing"],"summary":"Post Issue Credit","description":"Issue a new ``token_credit`` to a tenant.\n\nWraps the ``issue_credit`` primitive with the dual-write audit\npattern: ``admin_audit_log`` row via ``AuditRepository`` + broader\n``audit_log`` row via ``AuditLogger.log_admin_action``. Both writes\nhappen before commit so a partial failure rolls back atomically.","operationId":"post_issue_credit_v1_admin_tenants__tenant_id__issue_credit_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCreditRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IssueCreditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/transition-status":{"post":{"tags":["Admin · Tenant Billing"],"summary":"Post Transition Status","description":"Manually transition a tenant from one status to another.\n\nThe primitive ``transition_tenant_status`` requires\n``expected_from_status``. When the operator omits it, the route\nreads the current tenant_plan.status (without a row lock) and\npasses that as the expected value. When the operator supplies it,\nwe compare against the current status and return 409 on mismatch\nBEFORE calling the primitive — clearer error surface than parsing\nthe primitive's exception message.\n\nEither way, the primitive's own ``FOR UPDATE`` lock protects\nagainst concurrent transitions.","operationId":"post_transition_status_v1_admin_tenants__tenant_id__transition_status_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionStatusRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/force-delete":{"post":{"tags":["Admin · Tenant Billing"],"summary":"Post Force Delete","description":"Hard-delete a tenant's content + drop the Qdrant collection.\n\nPer founder Q5 ruling: this endpoint requires the tenant to\nalready be in ``status='deleted'``. Operators run /transition-\nstatus first to put the tenant in 'deleted', then call this\nendpoint to do the irreversible content + Qdrant purge. The two-\nstep flow keeps audit attribution clean — each step writes its\nown audit log row with its own ``reason``.\n\nMaps the primitive's ``ForceDeleteResult`` to HTTP:\n\n  * ``success=True``                              → 200\n  * reason ``no_active_tenant_plan``              → 404\n  * reason ``tenant_not_in_deleted_status:...``   → 400\n  * reason ``blocked_by_cancel_blockers``         → 400 + blocker\n    list (operator must pass ``bypass_blockers=true`` or resolve)\n  * reason starts with ``postgres_delete_failed:``→ 500\n\nAudit log written ONLY on success — failed precondition + blocker\ncases surface via the primitive's structured log + the HTTP error.","operationId":"post_force_delete_v1_admin_tenants__tenant_id__force_delete_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceDeleteRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceDeleteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants":{"get":{"tags":["Admin · Tenants","Admin · Tenants"],"summary":"List Tenants","description":"List every tenant ordered by name (ASC), with active plan_tier.\n\nCaller responsibility: this endpoint returns ALL tenants, including\nsuspended/deleted ones. The frontend filter typically renders them\nall (the operator may want to investigate suspended-tenant cost\nhistory). If a tenant is in 'deleted' status the cost-reporting\nendpoints will still serve its historical data — keep it visible.\n\n``plan_tier`` comes from the active ``tenant_plan`` row (filter\n``ended_at IS NULL`` — the canonical \"active plan\" predicate used\nby run_grandfathered_migration_sweep, run_free_high_engagement_detection,\ntransition_tenant_status, run_index_daily_accrual, et al.).\nA LEFT JOIN preserves tenants that don't yet have a plan row.","operationId":"list_tenants_v1_admin_tenants_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminTenantListResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/nightly/{job_name}/force-invoke":{"post":{"tags":["Admin · Nightly","Admin · Nightly"],"summary":"Force Invoke Nightly Job","description":"Trigger a nightly job out-of-band.\n\nSame fan-out path as the EventBridge Scheduler — the backend\ninvokes the nightly Lambda with ``{\"job_name\": ...}`` and the\nshim validates + spawns the ECS task. Returns 202 Accepted with\nthe ECS task ARN. No polling — operator follows the task via\nCloudWatch.\n\nErrors:\n  * 400 — job_name not in allowlist\n  * 502 — Lambda invoke failed (network, IAM, etc.)\n  * 500 — unexpected error from the Lambda's response payload","operationId":"force_invoke_nightly_job_v1_admin_nightly__job_name__force_invoke_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"job_name","in":"path","required":true,"schema":{"type":"string","title":"Job Name"}}],"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceInvokeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/nightly/allowlist":{"get":{"tags":["Admin · Nightly","Admin · Nightly"],"summary":"Get Nightly Allowlist","description":"Return which jobs are force-invokable + which require destructive\nconfirmation. Source of truth for the backoffice Run-now button.","operationId":"get_nightly_allowlist_v1_admin_nightly_allowlist_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NightlyAllowlistResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/health/deep":{"get":{"tags":["Admin · Deep Health"],"summary":"Health Deep","description":"Run every dependency probe in parallel, return a structured report.\n\nThe wall-clock budget is the runner's per-check 5 s × parallel\ngather, so worst case ~5 s if a single integration times out and\neverything else completes. The route does not impose its own outer\ntimeout — FastAPI's timeout (60 s ALB, 10 s Cloudflare) is wide\nenough that we never need it.","operationId":"health_deep_v1_admin_health_deep_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"When true, bypasses the 30 s Redis cache and runs every probe live. Use sparingly — 18 parallel external calls per request.","default":false,"title":"Refresh"},"description":"When true, bypasses the 30 s Redis cache and runs every probe live. Use sparingly — 18 parallel external calls per request."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Health Deep V1 Admin Health Deep Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/nightly-runs":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Nightly Runs","description":"Recent nightly job runs with optional ``failed_only`` filter.","operationId":"operational_nightly_runs_v1_admin_operational_nightly_runs_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}},{"name":"window_hours","in":"query","required":false,"schema":{"type":"integer","maximum":168,"minimum":1,"default":24,"title":"Window Hours"}},{"name":"failed_only","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Failed Only"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NightlyRunsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/connector-sync":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Connector Sync","description":"Per-connector sync state across every active tenant.","operationId":"operational_connector_sync_v1_admin_operational_connector_sync_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorSyncResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/oauth-health":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Oauth Health","description":"OAuth token bucket counts + connectors needing customer action.","operationId":"operational_oauth_health_v1_admin_operational_oauth_health_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthHealthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/webhook-delivery":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Webhook Delivery","description":"Last webhook event timestamp per provider.","operationId":"operational_webhook_delivery_v1_admin_operational_webhook_delivery_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookDeliveryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/queues":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Queues","description":"Current SQS depth + DLQ depth for the three production queues.\n\nDistinct endpoint from ``/health/deep``'s SQS check: the dashboard\ntile lives at 15 s TTL because queues spike fast and we want\noperators to notice within seconds, not 30+ seconds.","operationId":"operational_queues_v1_admin_operational_queues_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueueDepthResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/embedding-pipeline":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Embedding Pipeline","description":"Pending count + 24 h throughput + latency proxy.","operationId":"operational_embedding_pipeline_v1_admin_operational_embedding_pipeline_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmbeddingPipelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/synthesis-pipeline":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Synthesis Pipeline","description":"Partial synthesis observability — see Phase 0 §2 Section 8.\n\nLatency p50/p95 + citation accuracy are intentionally null until\nthe synthesis-logging schema revamp adds the supporting columns.\nThe dashboard tile renders them with a \"coming soon\" tooltip.","operationId":"operational_synthesis_pipeline_v1_admin_operational_synthesis_pipeline_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynthesisPipelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/operational/drift":{"get":{"tags":["Admin · Operational Dashboard"],"summary":"Operational Drift","description":"Latest drift snapshot per ``(tenant, connector)`` for one domain.\n\nDefault ``domain=iam_trust_policy`` covers Defense 4's first check.\nFuture drift checks (``oauth_tokens``, ``qdrant_schemas``, etc.)\nplug in by writing to the same ``drift_snapshots`` table with their\nown ``domain`` value — no schema or route changes required.\n\n5-minute cache (drift checks run daily; sub-hour resolution adds no\noperational value and burns Aurora read traffic).","operationId":"operational_drift_v1_admin_operational_drift_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"domain","in":"query","required":false,"schema":{"type":"string","default":"iam_trust_policy","title":"Domain"}},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Refresh"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DriftSectionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/feature-flags":{"get":{"tags":["Admin · Feature Flags"],"summary":"List Feature Flags","description":"List every registered flag + its current effective value.\n\nUsed by the Operational Switches tab to render the table.","operationId":"list_feature_flags_v1_admin_feature_flags_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlagsListResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/feature-flags/{key}":{"post":{"tags":["Admin · Feature Flags"],"summary":"Set Feature Flag Override","description":"Apply an override to a flag.\n\nRequires the operator to type the exact ``confirm_phrase_to_override``\nphrase. Rejects unknown keys with 404; invalid values with 400;\nincorrect confirmation with 400 + a clear message.","operationId":"set_feature_flag_override_v1_admin_feature_flags__key__post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlagMutationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Admin · Feature Flags"],"summary":"Clear Feature Flag Override","description":"Clear an override — flag reverts to its env-var default.\n\nRequires the operator to type ``restore {key}``. A no-op (no row to\ndelete) still returns 200 so the UI doesn't flag a benign double-click.","operationId":"clear_feature_flag_override_v1_admin_feature_flags__key__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClearOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FeatureFlagMutationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/feature-flags/{key}/scoped":{"get":{"tags":["Admin · Feature Flags"],"summary":"List Scoped Overrides","description":"List the per-tenant / per-user overrides for a flag (most-specific first).","operationId":"list_scoped_overrides_v1_admin_feature_flags__key__scoped_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopedOverridesListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Admin · Feature Flags"],"summary":"Set Scoped Override","description":"Set (upsert) a per-tenant or per-user override for a flag.","operationId":"set_scoped_override_v1_admin_feature_flags__key__scoped_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetScopedOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopedOverrideResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/feature-flags/{key}/scoped/{override_id}":{"delete":{"tags":["Admin · Feature Flags"],"summary":"Clear Scoped Override","description":"Delete a scoped override by id. Idempotent — returns deleted=false if\nthe row was already gone (benign double-click).","operationId":"clear_scoped_override_v1_admin_feature_flags__key__scoped__override_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}},{"name":"override_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Override Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"boolean"},"title":"Response Clear Scoped Override V1 Admin Feature Flags  Key  Scoped  Override Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/synthesis/debug/{query_log_id}":{"get":{"tags":["Admin · Synthesis"],"summary":"Get Synthesis Debug","description":"Inspect the synthesis observability for one logged query (404 if absent).","operationId":"get_synthesis_debug_v1_admin_synthesis_debug__query_log_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"query_log_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Query Log Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynthesisDebugResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/synthesis/rollout-metrics":{"get":{"tags":["Admin · Synthesis"],"summary":"Get Synthesis Rollout Metrics","description":"Aggregate synthesis-v2 adoption + signal-firing rates over a window.\n\nPowers the backoffice \"Synthesis v2 Rollout Status\" dashboard. ``from`` /\n``to`` are ISO-8601 timestamps; both default to the last 30 days when\nomitted. ``adoption_rate`` is computed over all logged queries in the\nwindow; ``grounding_ran`` / ``brain_memory_injected`` / supersession rates\nare over the synthesis-v2 subset only.","operationId":"get_synthesis_rollout_metrics_v1_admin_synthesis_rollout_metrics_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RolloutMetricsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/synthesis/evals":{"get":{"tags":["Admin · Synthesis"],"summary":"List Synthesis Evals","description":"List eval scorecards (newest first); empty when no evals have run here.","operationId":"list_synthesis_evals_v1_admin_synthesis_evals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvalRunsListResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/synthesis/evals/{version}":{"get":{"tags":["Admin · Synthesis"],"summary":"Get Synthesis Eval","description":"Full scorecard for one eval run (404 if the run dir is absent).","operationId":"get_synthesis_eval_v1_admin_synthesis_evals__version__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"version","in":"path","required":true,"schema":{"type":"string","title":"Version"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvalRunDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/synthesis/prompts":{"get":{"tags":["Admin · Synthesis"],"summary":"Get Synthesis Prompts","description":"Return the live region-routed synthesis prompts + model routing.\n\nPowers the Product Blueprint \"Synthesis Layer v2\" section — the operator\nsees the EXACT prompt text the model receives for Global (qwen) and EU/US\n(gpt), pulled through the same loader synthesize_v2 uses.","operationId":"get_synthesis_prompts_v1_admin_synthesis_prompts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SynthesisPromptsResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/maintenance/rechunk-by-chunker-version":{"post":{"tags":["Admin · Maintenance"],"summary":"Rechunk By Chunker Version","description":"Bulk-queue enterprise content pieces with stale chunker_version\nfor re-enrichment.\n\nPieces become candidates when at least one of their child chunks\ncarries a ``chunker_version`` in the supplied list AND the piece is\nin a re-enrichable status (not ``user_deleted``, not\n``enrichment_failed``, not soft-deleted, not removed).\n\nThe action only sets ``enrichment_due_at = now()`` — the existing\ndebounce worker picks the pieces up on its next ~60s sweep and\nroutes them through the standard re-enrichment pipeline (Qdrant\nchunk replacement, re-embed under the current model, re-index).\n\nDry-run mode (default) returns matched_count + sample_piece_ids\nwithout writing. Flip ``dry_run: false`` to actually queue.","operationId":"rechunk_by_chunker_version_v1_admin_maintenance_rechunk_by_chunker_version_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechunkByChunkerVersionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RechunkByChunkerVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/billing/provision-stripe-catalog":{"post":{"tags":["Admin · Billing · Provisioner"],"summary":"Provision Stripe Catalog Endpoint","description":"Re-run the Stripe Price catalog provisioner synchronously.\n\nOperator-facing companion to the nightly\n``stripe_catalog_provision`` job and the\n``scripts/provision_stripe_catalog`` CLI. All three call sites\nfunnel through the same ``run_provision_stripe_catalog`` action so\nbehaviour is identical regardless of trigger.\n\nReturns the per-product-kind counts from the underlying action's\n``ProvisioningReport`` plus a wall-clock duration. Counts reflect\nStripe-side calls fired (not net-new rows) — the action's Stripe\nidempotency keys make re-runs safe no-ops on the Stripe side, but\neach row still loops through the provisioner.","operationId":"provision_stripe_catalog_endpoint_v1_admin_billing_provision_stripe_catalog_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionStripeCatalogRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionStripeCatalogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/quality-gate":{"get":{"tags":["Admin · Quality Gate"],"summary":"Get Quality Gate Overview","description":"Return the operator-facing quality-gate overview.","operationId":"get_quality_gate_overview_v1_admin_quality_gate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QualityGateOverviewResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/cost-reporting/dashboard":{"get":{"tags":["Admin · Cost Reporting"],"summary":"Get Dashboard","description":"Cross-tenant dashboard — headline totals, daily series, top-10s.\n\nReads ``daily_token_rollup`` for the headline + per-day + top\ntenants. Reads ``llm_call_logs`` for top models (rollup has no\nmodel dimension; acceptable scale for an operator dashboard).\nCustomer-facing kT total joins ``token_consumption`` over the\nsame window.","operationId":"get_dashboard_v1_admin_cost_reporting_dashboard_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DashboardResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/tenants/{tenant_id}/usage":{"get":{"tags":["Admin · Cost Reporting"],"summary":"Get Tenant Usage","description":"Per-tenant deep-dive — reads ``llm_call_logs`` directly (real-time).","operationId":"get_tenant_usage_v1_admin_cost_reporting_tenants__tenant_id__usage_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/queries/{query_log_id}":{"get":{"tags":["Admin · Cost Reporting"],"summary":"Get Query Drill Down Endpoint","description":"Per-query drill-down — parent + every child LLM call.\n\nEach call response carries both the raw ``cost_usd_micros`` and a\npre-rendered ``cost_breakdown`` educational string showing the\narithmetic. Returns 404 when the query_log_id doesn't exist.","operationId":"get_query_drill_down_endpoint_v1_admin_cost_reporting_queries__query_log_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"query_log_id","in":"path","required":true,"schema":{"type":"string","title":"Query Log Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryDrillDownResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/queries":{"get":{"tags":["Admin · Cost Reporting"],"summary":"List Queries","description":"Paginated list of query_logs with per-row summary cost.\n\nFilters:\n  - ``tenant_id``: scope to a single tenant (None = cross-tenant)\n  - ``from`` / ``to``: inclusive date bounds on ``created_at``\n\nEach row carries enough to render a backoffice browse table without\na follow-up call: tenant name (resolved via JOIN), mode, status,\ncoverage level, ``sources_returned`` / ``input_tokens`` /\n``output_tokens`` (read directly from query_logs — v0.9 PR2\ninstrumentation), and the rolled-up total provider cost + LLM call\ncount from ``llm_call_logs``. The drill-down at ``/queries/{id}``\nreturns the per-call breakdown.","operationId":"list_queries_v1_admin_cost_reporting_queries_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"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/QueryListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/content":{"get":{"tags":["Admin · Cost Reporting"],"summary":"List Content Pieces","description":"Paginated list of content pieces with rolled-up ingestion cost.\n\nFilters: optional ``tenant_id`` (None = cross-tenant), optional\ninclusive date bounds on the piece's ``created_at``.\n\nEach row carries the per-piece total provider cost + LLM call\ncount rolled up from ``llm_call_logs`` where ``content_piece_id``\nmatches. The drill-down at ``/content/{id}`` surfaces the\nworkload-grouped breakdown (extraction, transcription, embedding,\nmetadata, taxonomy, relationship classification).","operationId":"list_content_pieces_v1_admin_cost_reporting_content_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"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/ContentListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/content/{content_piece_id}":{"get":{"tags":["Admin · Cost Reporting"],"summary":"Get Content Drill Down Endpoint","description":"Per-content drill-down. Returns 404 when the piece doesn't exist.","operationId":"get_content_drill_down_endpoint_v1_admin_cost_reporting_content__content_piece_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"content_piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Content Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ContentDrillDownResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-reporting/drift":{"get":{"tags":["Admin · Cost Reporting"],"summary":"Get Drift","description":"List ``(model, provider, tenant_id)`` triples flagged as unrated.\n\nMirrors the ``v_llm_call_logs_unrated`` SQL view's shape but takes\na caller-chosen window — the view itself is fixed to the last 24h.\nDefault window matches the view: trailing 24 hours.","operationId":"get_drift_v1_admin_cost_reporting_drift_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"To"}},{"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/DriftResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-catalog":{"get":{"tags":["Admin · Cost Catalog"],"summary":"Cost catalog snapshot","description":"Returns every entry across the 4 catalog YAMLs (LLM / Infrastructure / Third-Party / Plans), enriched with ``staleness_days`` per entry. The catalog is parsed fresh on every request; no caching.","operationId":"get_cost_catalog_v1_admin_cost_catalog_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CatalogResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/cost-catalog/export.xlsx":{"get":{"tags":["Admin · Cost Catalog"],"summary":"Cost catalog xlsx export","description":"Generates a 5-sheet xlsx (LLM / Infrastructure / Third-Party / Plans / README) from the catalog. **Take-offline only — re-imports are NOT supported.** The README sheet inside the file documents this constraint loudly.","operationId":"export_cost_catalog_xlsx_v1_admin_cost_catalog_export_xlsx_get","responses":{"200":{"description":"xlsx binary download.","content":{"application/json":{"schema":{}},"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/cost-catalog/live":{"get":{"tags":["Admin · Cost Catalog"],"summary":"Cost catalog live spend","description":"Phase 2 live-spend panel. Joins ``llm_call_logs`` × the LLM catalog (by ``workload``) × ``tenants`` to render actual per-workload, per-tenant, and per-day USD over the requested window. ``llm_call_logs.cost_usd_micros`` is the source of truth — no re-computation from the rate card here. Default window is the trailing 30 days; max 92 days.","operationId":"get_cost_catalog_live_v1_admin_cost_catalog_live_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"description":"Optional tenant filter (UUID). Scopes the entire payload — stats, tables, and the daily chart all narrow to this tenant.","title":"Tenant Id"},"description":"Optional tenant filter (UUID). Scopes the entire payload — stats, tables, and the daily chart all narrow to this tenant."},{"name":"workload","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional workload filter — exact match against ``llm_call_logs.workload``. Useful for drilling into one workload across all tenants.","title":"Workload"},"description":"Optional workload filter — exact match against ``llm_call_logs.workload``. Useful for drilling into one workload across all tenants."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LiveSpendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-catalog/assumptions":{"get":{"tags":["Admin · Cost Catalog"],"summary":"List cost-assumption sliders","description":"Returns every row in ``cost_assumption_overrides``. Ops-editable business assumptions (paying-tier mix, customer distribution, gross-margin target, etc.) — the slider half of the cost catalog architecture. Provider rates (LLM, AWS, third-party SaaS) live in YAML and are NOT returned here; use ``GET /v1/admin/cost-catalog`` for those.","operationId":"list_cost_assumptions_v1_admin_cost_catalog_assumptions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssumptionOverridesResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/cost-catalog/assumptions/{key}":{"patch":{"tags":["Admin · Cost Catalog"],"summary":"Update one cost-assumption slider value","description":"Sets ``value`` on a single ``cost_assumption_overrides`` row. The slider key must already exist (created by the migration seed); attempts to PATCH an unknown key return 404. Type mismatches between the request body and the row's ``value_type`` return 422 — a percent slider rejects a string body, a currency slider rejects a negative number, and so on.\n\nEvery successful PATCH writes a structured ``cost_assumption_updated`` audit-log entry.","operationId":"update_cost_assumption_v1_admin_cost_catalog_assumptions__key__patch","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":200,"description":"Slider key — must match an existing row. e.g. ``paying_tier_mix.team``.","title":"Key"},"description":"Slider key — must match an existing row. e.g. ``paying_tier_mix.team``."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateAssumptionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssumptionOverride"}}}},"404":{"description":"Slider key not found."},"422":{"description":"Value type mismatch."}}}},"/v1/admin/cost-catalog/infra-spend":{"get":{"tags":["Admin · Cost Catalog"],"summary":"Infrastructure spend rollup","description":"Sums ``infra_usage_daily.cost_estimate_usd`` per service over the trailing N days (default 30, max 365) and returns one row per service plus the window total. Backoffice cost dashboard consumes this side-by-side with the live LLM spend from PR #610's ``/live`` endpoint to render the full picture.","operationId":"get_infra_spend_v1_admin_cost_catalog_infra_spend_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":365,"minimum":1,"description":"Window size in days. Inclusive of today's UTC date.","default":30,"title":"Window Days"},"description":"Window size in days. Inclusive of today's UTC date."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InfraSpendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/cost-catalog/projections":{"get":{"tags":["Admin · Cost Catalog"],"summary":"Cost projections — unit economics + reconciliation","description":"Phase 3 projection math + drift reconciliation. Joins live ``llm_call_logs`` + ``infra_usage_daily`` + ``cost_assumption_overrides`` + ``effective_tenant_plan`` to produce four projection dimensions:\n\n  * **per_tenant** — COGS per tenant (LLM + allocated infra share) + 30d-rolling + revenue + margin %.\n  * **cogs_per_kt** — headline unit cost per Knowledge Token consumed.\n  * **per_tier** — aggregate revenue / COGS / per-seat figures for each (free / team / growth / enterprise) tier.\n  * **drift_warnings** — rows where actual COGS diverges from the projected COGS by more than the operator-tunable threshold (``cost_drift_warning_threshold_pct`` slider, default 10%).\n\nInfra spend is allocated per tenant **proportionally by LLM-query-volume share** — operators can read the assumption in the response's ``assumptions_snapshot`` field. Default window: trailing 30 UTC days ending today. Max 92 days.","operationId":"get_cost_projections_route_v1_admin_cost_catalog_projections_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"name":"top_n_tenants","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Cap on the per-tenant table size — operator dashboards rarely need more than the top 100. Per-tier rollup + drift warnings aggregate from the full set regardless.","default":100,"title":"Top N Tenants"},"description":"Cap on the per-tenant table size — operator dashboards rarely need more than the top 100. Per-tier rollup + drift warnings aggregate from the full set regardless."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Cost Projections Route V1 Admin Cost Catalog Projections Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/feedback/aggregate":{"get":{"tags":["Admin · Feedback","Admin · Feedback"],"summary":"Get Aggregate","description":"Composite dashboard payload for the backoffice Feedback tab.\n\nHeader stats (thumb rate 7d, total 30d, grounding traffic-light)\n+ daily trend over 30 days + reason distribution over 7 days +\nby-model + by-reranker breakdowns over 7 days. Cross-tenant —\nauth gates access, no tenant filter on the queries.","operationId":"get_aggregate_v1_admin_feedback_aggregate_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminAggregateResponse"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/feedback/recent":{"get":{"tags":["Admin · Feedback","Admin · Feedback"],"summary":"List Recent","description":"Paginated recent feedback rows.\n\nCross-tenant by default; ``tenant_id=`` query param scopes down\nwhen an operator wants a single-tenant view. ``rating`` and\n``reason`` filters narrow further. Sort: created_at DESC (most\nrecent first), backed by ix_answer_feedback_tenant_created /\nix_answer_feedback_rating_created indexes.","operationId":"list_recent_v1_admin_feedback_recent_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"}},{"name":"rating","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(thumbs_up|thumbs_down)$"},{"type":"null"}],"title":"Rating"}},{"name":"reason","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Reason"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecentFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/feedback/ui-bug-queue":{"get":{"tags":["Admin · Feedback","Admin · Feedback"],"summary":"Get Ui Bug Queue","description":"UI-bug feedback rows for operator triage.\n\nSurfaces rows where the user reported a UI bug (rating='thumbs_down'\nAND 'ui_bug' in reason_codes). Different operator workflow from\nthe general recent-rows table — these are product-backlog items,\nnot training signal.","operationId":"get_ui_bug_queue_v1_admin_feedback_ui_bug_queue_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UiBugQueueResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/email-kill-switches":{"get":{"tags":["Admin · Email kill switches","admin"],"summary":"List Kill Switches","description":"List every active kill switch — backoffice Emails page loads this\non render to mark each template card as enabled / disabled.","operationId":"list_kill_switches_v1_admin_email_kill_switches_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KillSwitchListOut"}}}}},"security":[{"APIKeyHeader":[]}]},"post":{"tags":["Admin · Email kill switches","admin"],"summary":"Create Kill Switch","description":"Disable a template. Idempotent: toggling the same template off\ntwice refreshes the row's ``disabled_at`` + ``reason`` but doesn't\nfail.","operationId":"create_kill_switch_v1_admin_email_kill_switches_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KillSwitchCreateIn"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KillSwitchOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/admin/email-kill-switches/{template_id}":{"delete":{"tags":["Admin · Email kill switches","admin"],"summary":"Delete Kill Switch","description":"Re-enable a template. 404 if there was no active kill switch\n(idempotent re-enable is fine; we surface the no-op as 404 so the\nUI can refresh state if it was stale).","operationId":"delete_kill_switch_v1_admin_email_kill_switches__template_id__delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/email-templates/{template_id}/override":{"get":{"tags":["Admin · Email Templates","Admin · Email Templates"],"summary":"Get Override","description":"Return the live override + the manifest entry side-by-side.\n\nThe backoffice editor uses both: the override is shown as the\ncurrent editable content; the manifest entry is shown as the\n\"factory default\" + provides the required-token list.\n\nReturns ``override=None`` if no row exists. Returns\n``manifest=None`` if the manifest hasn't been loaded yet or the\ntemplate_id isn't in it — operators can still author an override\non an empty manifest (validation falls back to Jinja2-parse only).","operationId":"get_override_v1_admin_email_templates__template_id__override_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideGetOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Admin · Email Templates","Admin · Email Templates"],"summary":"Put Override","description":"Save or update an override.\n\nValidation chain:\n  1. ``subject`` + ``html`` non-empty (Pydantic + the validator).\n  2. Manifest-declared required tokens still present in the new\n     body (subject ∪ preview_text ∪ html). Looks up the manifest\n     entry via the renderer.\n  3. Jinja2 parse of subject / preview_text / html.\n\nReturns ``201`` on first save (no prior row), ``200`` on update.\nThe renderer's override cache is invalidated immediately so the\nnext ``render()`` picks up the change without waiting for the\nTTL.","operationId":"put_override_v1_admin_email_templates__template_id__override_put","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideSaveIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Admin · Email Templates","Admin · Email Templates"],"summary":"Delete Override","description":"Drop the live override. The next render falls back to S3.\n\nHistory rows persist — the audit trail outlives the live row.\nReturns 404 if no override existed (idempotent re-disable would\nconfuse the UI's \"delete confirmed\" state; surface as 404 so the\nfront-end can refresh stale state).","operationId":"delete_override_v1_admin_email_templates__template_id__override_delete","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/email-templates/{template_id}/override/history":{"get":{"tags":["Admin · Email Templates","Admin · Email Templates"],"summary":"List Override History","description":"Return every history row for ``template_id``, newest first.\n\nBacked by the composite index on the history table — single\nindex scan, no filesort. Returns an empty ``items`` list if\nthe template has never been edited.","operationId":"list_override_history_v1_admin_email_templates__template_id__override_history_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideHistoryOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/email-templates/{template_id}/preview":{"post":{"tags":["Admin · Email Templates","Admin · Email Templates"],"summary":"Preview Override","description":"Render the proposed override with a sample context. No persistence.\n\nValidation rules mirror PUT — subject/html non-empty, Jinja2\nparses, manifest-required tokens covered. The render fills any\ntoken NOT provided in ``sample_context`` with the sentinel\n``<sample:{token}>`` so operators can see exactly where each\ntoken lands without having to populate the full context.","operationId":"preview_override_v1_admin_email_templates__template_id__preview_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"template_id","in":"path","required":true,"schema":{"type":"string","title":"Template Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewIn"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/surveys/next_eligible":{"get":{"tags":["Surveys","Surveys"],"summary":"Ask the orchestrator whether to show a survey to this user now","description":"The frontend never decides to show a survey directly — it always\ncalls here.\n\nReturns ``instrument: None`` when no survey should be shown. The\n``reason`` field is for logging / self-tuning visibility; the\nfrontend should not branch on it.\n\n``feature`` (CSAT) / ``task`` (CES) are the per-context\ndiscriminators threaded into trigger_context. Their presence is\nREQUIRED for the corresponding instrument to fire — the\norchestrator fails closed on missing context so a misconfigured\nfrontend can't flatten all CSAT/CES cooldown buckets into one.","operationId":"next_eligible_v1_surveys_next_eligible_get","parameters":[{"name":"trigger_event","in":"query","required":true,"schema":{"type":"string","title":"Trigger Event"}},{"name":"feature","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Feature"}},{"name":"task","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Task"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NextEligibleResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/surveys/{instrument}/submit":{"post":{"tags":["Surveys","Surveys"],"summary":"Submit a survey response","description":"Record a user's score + optional comment.\n\nOn replay (same ``idempotency_key`` already exists) returns 409\nwith ``duplicate=True`` and the original ``record_id``.","operationId":"submit_survey_v1_surveys__instrument__submit_post","parameters":[{"name":"instrument","in":"path","required":true,"schema":{"type":"string","title":"Instrument"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSurveyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitSurveyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/surveys/{instrument}/dismiss":{"post":{"tags":["Surveys","Surveys"],"summary":"Record a survey dismissal","description":"Record a dismissal. Returns 200 (not 409) on idempotent replay\n— dismissals are a self-tuning signal, not a state mutation\ncustomers retry intentionally.","operationId":"dismiss_survey_v1_surveys__instrument__dismiss_post","parameters":[{"name":"instrument","in":"path","required":true,"schema":{"type":"string","title":"Instrument"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissSurveyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissSurveyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/surveys/preferences":{"get":{"tags":["Surveys","Surveys"],"summary":"Read the authenticated member's survey orchestrator preferences","description":"Return the current ``hard_opt_out`` flag for this member.\n\nReturns ``hard_opt_out=False`` when no ``user_survey_global_state``\nrow exists yet — the orchestrator treats a missing row as opt-in by\ndefault, so the API contract follows.","operationId":"get_survey_preferences_v1_surveys_preferences_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyPreferencesResponse"}}}}}},"put":{"tags":["Surveys","Surveys"],"summary":"Update the authenticated member's survey orchestrator preferences","description":"Flip the ``hard_opt_out`` flag for this member.\n\nLazily creates the ``user_survey_global_state`` row if it doesn't\nexist yet. The orchestrator checks ``hard_opt_out`` as the FIRST\ngate in its eligibility cascade, so flipping this to ``True`` stops\nany future survey impression for this member immediately.","operationId":"update_survey_preferences_v1_surveys_preferences_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSurveyPreferencesRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SurveyPreferencesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/slack/events":{"post":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Slack Events","description":"Slack Events API webhook — signature-verified, fails closed.\n\nHandles two Slack event envelopes:\n\n* ``url_verification`` (one-shot at Slack App install): responds\n  with ``{\"challenge\": <echoed challenge string>}`` so Slack\n  confirms our endpoint. Returns 400 if the challenge is missing\n  or non-string.\n\n* ``event_callback`` (runtime, e.g. ``app_mention``): ACKs with\n  ``{\"ok\": true}`` immediately. The inner event is logged but\n  not yet dispatched — dispatch wiring is added when\n  ``handle_mention`` action ships.\n\nAll other event types are logged and ACKed so Slack doesn't\nretry noisy categories (e.g. event-type additions we don't yet\nhandle).\n\nNever returns anything other than 200 for a valid-signed request;\nSlack treats non-2xx as failure and retries up to 3 times. A 401\nis only emitted on signature failure, where retrying won't help\nanyway (the secret is wrong).","operationId":"slack_events_v1_integrations_slack_events_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Slack Events V1 Integrations Slack Events Post"}}}}}}},"/v1/integrations/slack/commands":{"post":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Slack Commands","description":"Slack slash-command webhook for ``/quelvio <query>``.\n\nSignature-verified, fails closed on forged or stale requests.\nEmpty query → synchronous ephemeral usage hint. Non-empty query →\nsynchronous \"Searching…\" ephemeral ack + background task that\nreplaces the ack with the final Block Kit answer via\n``response_url`` (CEO ruling Q1: slash commands are ephemeral —\nonly the invoker sees the reply).\n\nWhy async delivery vs synchronous answer:\n    Slack enforces a 3-second timeout on slash-command responses.\n    Our pipeline is usually well under 1s but Qdrant contention\n    or a slow upstream LLM call can push past 3s. Async via\n    ``response_url`` gives 30 minutes of headroom with no UX\n    regression — the user sees \"Searching…\" flip to the answer.","operationId":"slack_commands_v1_integrations_slack_commands_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Slack Commands V1 Integrations Slack Commands Post"}}}}}}},"/v1/integrations/slack/installs":{"get":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"List Installs","description":"List the calling tenant's active Slack Query Bot installs.\n\nUsed by the Sources page to render the bot card. Returns the\ninstall state, workspace name, installer email — everything the\nUI needs to show \"active\" + \"connected by alice@acme.test\" or to\nfall back to the \"Install\" CTA when the list is empty.\n\nBot tokens are NEVER returned by this endpoint — the response\ndeliberately excludes them. Revoked installs are also excluded\n(they're treated as \"not installed\" by the UI).\n\nAuth: any active TenantMember can read their tenant's install\nlist (admins, managers, members alike) — knowing the bot is\ninstalled isn't sensitive. Disconnect is admin-only (see DELETE).","operationId":"list_installs_v1_integrations_slack_installs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Installs V1 Integrations Slack Installs Get"}}}}}}},"/v1/integrations/slack/install/{install_id}":{"delete":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Disconnect Install","description":"Soft-delete a Slack Query Bot install (admin only).\n\nSets ``status='revoked'``, stamps ``revoked_at``, and flips the\ntenant's ``enabled_integrations[\"slack_query_bot\"]`` flag to\nfalse. The Slack ``auth.revoke`` API is NOT called — the install\nbecomes dormant on Quelvio's side without affecting the workspace\n(CEO ruling Batch 1.9). Re-install via the OAuth flow is a single\nSources-page click.\n\nIdempotent: re-calling on an already-revoked install returns\n204 cleanly. The frontend can safely retry on network errors.\n\nTenant scoping: cross-tenant disconnect attempts return 404 —\nsame response as install-id-not-found, to prevent enumerating\ninstall ids across tenants via the disconnect endpoint.\n\nReturns 204 No Content on success (and on idempotent re-call).","operationId":"disconnect_install_v1_integrations_slack_install__install_id__delete","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Install Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/slack/oauth/install-url":{"get":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Install Url","description":"Return the Slack OAuth V2 authorize URL as JSON.\n\nThe v0.5.11 Sources page originally navigated the browser directly\nto ``/oauth/install``, which 302s to Slack. That broke for two\nreasons: (1) the frontend used a relative href so the browser\nhit ``enterprise.quelvio.com/v1/…`` (Next.js 404); (2) even with\nthe absolute host, the cross-origin browser navigation cannot send\nthe Clerk Bearer token, so the backend rejects with 401.\n\nThis endpoint matches the pattern used by every content connector\n(``/v1/enterprise/connect/{box,drive,…}``): the frontend\nauthenticates with Bearer, receives ``{auth_url: ...}``, then\nperforms ``window.location.href = auth_url`` to Slack directly.\n\nAuth + role checks mirror ``install_redirect`` via\n``_build_slack_install_url``.","operationId":"install_url_v1_integrations_slack_oauth_install_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Install Url V1 Integrations Slack Oauth Install Url Get"}}}}}}},"/v1/integrations/slack/oauth/install":{"get":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Install Redirect","description":"Legacy browser-navigation install entrypoint.\n\nKept for backward compatibility — the v0.5.11.1 Sources page\nuses ``/oauth/install-url`` instead (see that handler's docstring\nfor the rationale). This endpoint only works when the caller can\nsend an ``Authorization: Bearer`` header, which browser\nnavigations cross-origin cannot. Left in place so any\nserver-to-server caller that discovered this path still works.\n\nSide effects: none. State TTL is 10 minutes; if the admin\nabandons the flow mid-consent it simply expires.","operationId":"install_redirect_v1_integrations_slack_oauth_install_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/integrations/slack/oauth/callback":{"get":{"tags":["Integrations · Slack Query Bot","Integrations · Slack Query Bot"],"summary":"Install Callback","description":"OAuth V2 callback — verify state, exchange code, persist install.\n\nReaches us via Slack's redirect (``settings.slack_query_bot_redirect_uri``).\nNo Quelvio auth header — authorization is carried in the signed\n``state`` parameter. Every failure path redirects to the Sources\npage with ``install_error=<code>`` EXCEPT signature-verification\nfailures, which return a real 401 (a 302-with-error on forged\nstate would hide the security event from monitoring).","operationId":"install_callback_v1_integrations_slack_oauth_callback_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","description":"Signed state parameter","default":"","title":"State"},"description":"Signed state parameter"},{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Slack OAuth V2 code","default":"","title":"Code"},"description":"Slack OAuth V2 code"},{"name":"error","in":"query","required":false,"schema":{"type":"string","description":"Slack-reported error code (e.g. access_denied) when admin declines","default":"","title":"Error"},"description":"Slack-reported error code (e.g. access_denied) when admin declines"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/teams/messages":{"post":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"Teams Messages","description":"Bot Framework webhook — JWT-verified, fails closed on bad tokens.\n\nPhase 1 implementation: authenticate the activity, resolve the\nAzure tenant → Quelvio tenant via ``TeamsBotInstall``, log the\noutcome, and 200-ACK. The Phase 3 session wires the real\n``handle_message`` dispatch inside the ``_logic`` callback without\nchanging this route.\n\nStatus codes:\n    * 200 — everything healthy, or any silent-drop scenario (kill\n      switch off, unknown azure tenant, paused/revoked install,\n      non-Teams activity with no ``channelData.tenant.id``). Bot\n      Framework does not retry on 200.\n    * 400 — malformed body or body is not a parseable Activity.\n    * 401 — missing/invalid Authorization bearer token.\n    * 403 — JWT signature valid but claims fail validation (bad\n      audience, expired, wrong channel endorsement, etc.).\n\nGlobal kill switch:\n    ``settings.enable_teams_query_bot`` gates every path. When\n    false we return 200 (silent drop) — NOT 503 — so Bot Framework\n    stops retrying. Observability comes through the structured\n    ``teams_query_bot_global_kill_switch_engaged`` log event.\n\nAuth-first ordering rationale:\n    We validate the JWT BEFORE the kill-switch check so that\n    forged requests are rejected with 401/403 even during kill\n    switch — otherwise an attacker could use the kill-switch\n    window to probe for the endpoint without any rejection\n    signal. The cost is cheap: JWT validation is ~1ms with a\n    warm JWKS cache.","operationId":"teams_messages_v1_integrations_teams_messages_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Teams Messages V1 Integrations Teams Messages Post"}}}}}}},"/v1/integrations/teams/oauth/install-url":{"get":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"Install Url","description":"Return the Azure AD admin-consent URL as JSON.\n\nCanonical install entry point (matches PR #66 Slack Query Bot\npattern + the 8 content-connector pattern). The frontend\nauthenticates with Bearer, receives ``{auth_url: ...}``, then\nperforms ``window.location.href = auth_url`` — a cross-origin\nbrowser navigation cannot carry the Bearer token, so the backend\nwould reject a server-side 302 with 401. This JSON shape decouples\nauth from navigation.\n\nAuth: enterprise SSO JWT (or enterprise API key) via\n``get_enterprise_auth``. Role: ``admin`` only.","operationId":"install_url_v1_integrations_teams_oauth_install_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Install Url V1 Integrations Teams Oauth Install Url Get"}}}}}}},"/v1/integrations/teams/oauth/install":{"get":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"Install Redirect","description":"Legacy browser-navigation install entrypoint.\n\nKept for backward compatibility — the Sources page uses\n``/oauth/install-url`` instead. This endpoint only works when the\ncaller can send an ``Authorization: Bearer`` header, which\ncross-origin browser navigations cannot. Left in place so any\nserver-to-server caller that discovered this path still works.\n\nSide effects: none. State TTL is 10 minutes; if the admin\nabandons the flow mid-consent it simply expires.","operationId":"install_redirect_v1_integrations_teams_oauth_install_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/integrations/teams/oauth/callback":{"get":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"Install Callback","description":"Admin-consent callback — verify state, upsert install record.\n\nAzure AD's ``/organizations/adminconsent`` endpoint returns:\n\n* On success: ``?admin_consent=True&tenant=<guid>&state=<state>``\n* On decline / error: ``?error=<code>&error_description=<desc>&state=<state>``\n\nEvery failure path redirects to Sources with a clean marker — never\n422. Signature-verification failures return a real 401 (tampered /\nforged state is a security event, not a UX event; a 302-with-error\nwould hide it from monitoring).\n\nKill-switch behaviour:\n    Runs BEFORE any state verification. If the kill switch is off\n    we redirect to Sources with ``?oauth_error=feature_disabled``\n    so the admin sees a clean banner even if they were mid-consent\n    when ops flipped the flag.","operationId":"install_callback_v1_integrations_teams_oauth_callback_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","description":"Signed state from the install redirect","default":"","title":"State"},"description":"Signed state from the install redirect"},{"name":"tenant","in":"query","required":false,"schema":{"type":"string","description":"Azure AD tenant GUID (success path only)","default":"","title":"Tenant"},"description":"Azure AD tenant GUID (success path only)"},{"name":"admin_consent","in":"query","required":false,"schema":{"type":"string","description":"'True' on successful admin consent (Azure AD literal)","default":"","title":"Admin Consent"},"description":"'True' on successful admin consent (Azure AD literal)"},{"name":"error","in":"query","required":false,"schema":{"type":"string","description":"Azure AD error code (e.g. access_denied, invalid_client)","default":"","title":"Error"},"description":"Azure AD error code (e.g. access_denied, invalid_client)"},{"name":"error_description","in":"query","required":false,"schema":{"type":"string","description":"Human-readable Azure AD error description","default":"","title":"Error Description"},"description":"Human-readable Azure AD error description"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/teams/installs":{"get":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"List Installs","description":"List the calling tenant's active Teams Query Bot installs.\n\nUsed by the Sources page to render the bot card. Returns install\nstate + authed-user email — everything the UI needs to show \"active\"\n+ \"connected by <email>\" or to fall back to the \"Install\" CTA when\nthe list is empty. Encrypted config blob is NEVER returned (the Q5\nJSONB slot is server-side state only).\n\nAuth: any active TenantMember can read their tenant's install list\n(admins, managers, members alike) — knowing the bot is installed\nisn't sensitive. Disconnect is admin-only (see DELETE).","operationId":"list_installs_v1_integrations_teams_installs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Installs V1 Integrations Teams Installs Get"}}}}}}},"/v1/integrations/teams/install/{install_id}":{"delete":{"tags":["Integrations · Teams Query Bot","Integrations · Teams Query Bot"],"summary":"Disconnect Install","description":"Soft-delete a Teams Query Bot install (admin only).\n\nSets ``status='revoked'``, stamps ``revoked_at``, and flips the\ntenant's ``enabled_integrations[\"teams_query_bot\"]`` flag to false.\nNo Azure AD API call — consent revocation on Microsoft's side would\nrequire admin-tenant Graph credentials we don't hold. The install\nbecomes dormant on Quelvio's side; the Teams admin can fully\nrevoke from Teams Admin Center if they choose.\n\nIdempotent: re-calling on an already-revoked install returns 204\ncleanly. The frontend can retry on network errors.\n\nTenant scoping: cross-tenant disconnect attempts return 404 — same\nresponse as install-id-not-found, to prevent enumerating install\nids across tenants via the disconnect endpoint.\n\nReturns 204 No Content on success (and on idempotent re-call).","operationId":"disconnect_install_v1_integrations_teams_install__install_id__delete","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Install Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/notion/oauth/install-url":{"get":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"Install Url","description":"Return the Notion OAuth consent URL as JSON.\n\nFrontend pattern: GET this endpoint, read ``auth_url``, then set\n``window.location.href = auth_url``. Matches E1/E2 conventions.","operationId":"install_url_v1_integrations_notion_oauth_install_url_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Install Url V1 Integrations Notion Oauth Install Url Get"}}}}}}},"/v1/integrations/notion/oauth/install":{"get":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"Install Redirect","description":"302 to the Notion consent page. Convenience for anchor-tag installs.","operationId":"install_redirect_v1_integrations_notion_oauth_install_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/integrations/notion/oauth/callback":{"get":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"Install Callback","description":"Notion OAuth callback — verify state, exchange code, persist install.","operationId":"install_callback_v1_integrations_notion_oauth_callback_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"type":"string","description":"Signed state parameter","default":"","title":"State"},"description":"Signed state parameter"},{"name":"code","in":"query","required":false,"schema":{"type":"string","description":"Notion OAuth code","default":"","title":"Code"},"description":"Notion OAuth code"},{"name":"error","in":"query","required":false,"schema":{"type":"string","description":"Notion-reported error code if the user declined","default":"","title":"Error"},"description":"Notion-reported error code if the user declined"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/notion/link-preview/unfurl":{"post":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"Link Preview Unfurl","description":"Handle a Notion Link Preview unfurl callback.\n\nC2 fix (v0.5.24): the route is gated by\n:data:`notion_link_preview_verifier`. The verifier reads the\n``Authorization: Bearer <token>`` header, hashes the token, and\nlooks up the install row by the indexed ``access_token_sha256``\ncolumn. Unknown / inactive / missing-bearer requests get a 401\nbefore reaching this handler. The verified install ID is stashed\non ``request.state.notion_link_preview_install_id`` for the\nhandler to audit-log.\n\nPre-fix behaviour was documented as \"trust-but-verify; we don't\ninspect the bearer\" — the audit's §3.12 surfaced this as a\ncritical unverified-accept finding (cost amplification + share-\ntoken probe oracle). The verifier closes both vectors.\n\nReturns 200 with the Notion unfurl JSON body on every success\npath, including \"not unfurlable\" for non-Quelvio URLs.","operationId":"link_preview_unfurl_v1_integrations_notion_link_preview_unfurl_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnfurlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/notion/installs":{"get":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"List Installs","description":"List Link Preview installs for the caller's tenant (Sources page).","operationId":"list_installs_v1_integrations_notion_installs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Installs V1 Integrations Notion Installs Get"}}}}}}},"/v1/integrations/notion/install/{install_id}":{"delete":{"tags":["Integrations · Notion Link Preview","Integrations · Notion Link Preview"],"summary":"Disconnect Install","description":"Admin-only: soft-revoke a Link Preview install.","operationId":"disconnect_install_v1_integrations_notion_install__install_id__delete","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Install Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/webhooks/stripe":{"post":{"tags":["Webhooks · Stripe"],"summary":"Stripe Webhook","description":"Receive a Stripe webhook event.\n\nOrder of operations (each step's failure mode is non-overlapping):\n  1. Read raw payload + Stripe-Signature header.\n  2. Read STRIPE_WEBHOOK_SECRET from settings.\n  3. Verify signature via stripe.Webhook.construct_event.\n  4. Dedup via processed_events.\n  5. Dispatch to registered handler (or 200 if unhandled).\n  6. Return 200 on success.","operationId":"stripe_webhook_webhooks_stripe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/api/billing/features":{"get":{"tags":["Billing · Features (WS-15)"],"summary":"Get Billing Features","description":"Return the tenant's resolved capability map.\n\nResolves :class:`FeatureGate` once via the request-scoped\ndependency (single DB read), then materialises every enumerated\n:class:`BillingCapability` value into the response payload.\n\nTenant identity comes from the authenticated session — never from\nthe request body or query string (CLAUDE.md anti-pattern).","operationId":"get_billing_features_api_billing_features_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingFeaturesResponse"}}}}}}},"/api/billing/corpus-tier/options":{"get":{"tags":["Billing · Corpus (WS-2)"],"summary":"List corpus tier options for the tenant","description":"Return the catalogued ladder for the tenant's (plan_tier, region)\nplus the currently-selected option.","operationId":"getCorpusTierOptions","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorpusTierOptionsResponse"}}}}}}},"/api/billing/corpus-tier/change":{"post":{"tags":["Billing · Corpus (WS-2)"],"summary":"Change to a corpus tier","description":"Owner or Admin only. Upgrades take effect immediately; downgrades\nthat fit current usage are scheduled for next period boundary;\ndowngrades exceeding usage return 422 ``corpus_downgrade_blocked``.\n\nOD-3 (Open Decision 3): when a downgrade is scheduled and usage\nlater grows past the target, ingestion is blocked\n(``corpus_tier_exceeded``) — see ``gate_ingestion``. This endpoint\nonly handles the *change request* itself.","operationId":"changeCorpusTier","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorpusTierChangeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorpusTierChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/corpus-usage":{"get":{"tags":["Billing · Corpus (WS-2)"],"summary":"Current corpus usage state","description":"Return the corpus-usage snapshot for the tenant.","operationId":"getCorpusUsage","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorpusUsageResponse"}}}}}}},"/api/regions/available":{"get":{"tags":["Billing · Regions (WS-9)"],"summary":"Region picker options (signup)","description":"Return the three v0.9 regions for the signup region picker.\n\nPublic endpoint — no auth. The caller is mid-signup and does not\nyet have a Clerk-bound tenant.","operationId":"getAvailableRegions","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableRegionsResponse"}}}}}}},"/api/billing/packs/purchase":{"post":{"tags":["Billing · Packs (WS-4)"],"summary":"Purchase an at-cap pack","description":"Owner or Admin only. Idempotent on the customer-provided\n``idempotency_key``: a duplicate submit within the F-2 op log\nwindow returns 200 with ``idempotent_replay=True``.\n\nErrors:\n- 402 ``stripe_charge_failed`` — Stripe declined the charge.\n- 422 ``query_type_not_available_on_plan`` — pack_type not\n  catalogued for the tenant's (plan, region) tuple.\n- 503 ``stripe_charge_failed`` (``reason=pack_price_not_provisioned``)\n  — active Stripe-mode pack Price is missing from\n  ``stripe_price_catalog``.","operationId":"purchasePack","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackPurchaseRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackPurchaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/packs/auto-renew":{"post":{"tags":["Billing · Packs (WS-4)"],"summary":"Toggle auto-renew for a pack type","description":"Owner or Admin only. Sets ``auto_renew_enabled`` on every active\nbundle of the given pack_type for the tenant.\n\nThe boolean returned is the resolved state — i.e., it equals the\nrequest's ``enabled`` (no server-side override). Bundles affected\nis logged but not in the response (the Contract Lock shape omits\nit).","operationId":"configurePackAutoRenew","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackAutoRenewRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackAutoRenewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/packs/inventory":{"get":{"tags":["Billing · Packs (WS-4)"],"summary":"Active pack inventory","description":"Any member can read. Returns active (not-expired, not-exhausted)\nbundles + the per-pack-type auto_renew_settings aggregate.","operationId":"getPackInventory","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackInventoryResponse"}}}}}}},"/backoffice/tenant/{tenant_id}/bundle/grant":{"post":{"tags":["Backoffice · Packs (WS-4)"],"summary":"Grant a pack to a tenant (no Stripe charge)","description":"Admin-only. Grants ``quantity`` bundles of ``pack_type`` to the\ntenant with NO Stripe charge. ``source='backoffice_grant'`` —\nsurfaces clearly in the audit trail and in PackPurchasedEvent\nconsumers.\n\nThe grant's natural key embeds a fresh UUID per request so\nrepeated grants (intentional or accidental) create separate\nbundle rows — backoffice grants are NOT idempotent by design.\nThe audit log row is the canonical audit trail.","operationId":"backofficeGrantBundle","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleGrantRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackPurchaseResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/bundle/revoke":{"post":{"tags":["Backoffice · Packs (WS-4)"],"summary":"Revoke an active bundle","description":"Admin-only. Marks a bundle exhausted with auto_renew disabled.\nRefund handling (writing a ``tenant_credits`` row for the unused\nportion) is OUT OF SCOPE for this PR — captured as a v0.9\nfollow-up. The audit log records the bundle's\n``queries_remaining`` at revoke time so the operator can compute\n+ grant the credit manually until the credit-ledger wiring lands.\n\nTenant scope is enforced at the repo layer — a stale bundle_id\nfrom another tenant returns 404.","operationId":"backofficeRevokeBundle","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BundleRevokeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__domains__enterprise__billing__presentation__pack_backoffice_routes__BackofficeOverrideAck"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/bundle-inventory":{"get":{"tags":["Backoffice · Packs (WS-4)"],"summary":"Bundle inventory for a tenant (backoffice)","description":"Admin-only read of bundle inventory. Same shape as the\ncustomer-facing ``GET /api/billing/packs/inventory`` so the\nbackoffice can reuse the same UI components.\n\nRead-only — no audit log row (reads are not audited at the row\nlevel in Phase A's pattern; access logging happens at the\nrequest/firewall layer).","operationId":"backofficeBundleInventory","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PackInventoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/payg/configure":{"post":{"tags":["Billing · PAYG (WS-5)"],"summary":"Enable/disable PAYG and set spend cap","description":"Owner only — flipping PAYG is a money-affecting setting.\n\nF-8: raising the cap is immediate for new queries (the gate reads\nthe new value on the next call). Lowering below current spend\nblocks new queries; accrued spend is still owed at next-invoice\ntime.\n\nPhase A's 2FA-cancel-blocker is a STUB (no ``last_2fa`` column\nyet) — Owner-role enforcement is structural, 2FA-within-15-min\nships with v0.3 backoffice auth.","operationId":"configurePayg","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaygConfigureRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaygConfigureResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/payg/status":{"get":{"tags":["Billing · PAYG (WS-5)"],"summary":"PAYG state for the tenant","description":"Any-member read. Returns the resolved PAYG state — enabled\nflag, cap, spent, period boundaries, threshold-alert state.\n\n``threshold_alerts`` carries booleans (was each threshold's email\nfired this period?). The underlying column stores ISO timestamps;\nthe route projects to booleans per the Contract Lock shape.","operationId":"getPaygStatus","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaygStatusResponse"}}}}}}},"/api/billing/daily-caps":{"get":{"tags":["Billing · Caps + Estimate (Wave 5)"],"summary":"Daily kT pool state","description":"Return today's daily kT pool state for the tenant or the caller.\n\nPersonal scope (``?scope=me``) is filtered to the calling user's\nconsumption row when the tenant uses per-seat aggregation. The\nresponse carries ``scope='user'`` on success and ``scope='tenant'``\nwhen the request falls back (Free, or when no member_id is bound\nto the auth context — e.g. raw API key calls).\n\nTenant-scope gating: ``scope=tenant`` is the workspace-wide view\nand is gated to owners/admins. A member who forges\n``?scope=tenant`` is silently downgraded to their personal slice\n(we don't 403 because /settings/usage's \"Me\" tab is the legitimate\nlanding surface for members; the response's ``scope`` field\ntruthfully reports what was actually returned). On Free, where\npool aggregation is per-tenant, the personal slice would have\nfallen back anyway — this gate just makes the downgrade explicit\ninstead of relying on aggregation_level.","operationId":"getDailyCaps","parameters":[{"name":"scope","in":"query","required":false,"schema":{"enum":["tenant","me"],"type":"string","description":"'tenant' (default) returns the tenant-wide pool. 'me' returns the calling user's personal slice on per-seat tenants; Free (per-tenant aggregation) silently falls back to the shared tenant view because the pool isn't separable per user.","default":"tenant","title":"Scope"},"description":"'tenant' (default) returns the tenant-wide pool. 'me' returns the calling user's personal slice on per-seat tenants; Free (per-tenant aggregation) silently falls back to the shared tenant view because the pool isn't separable per user."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DailyCapsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/estimate":{"get":{"tags":["Billing · Caps + Estimate (Wave 5)"],"summary":"Estimated next invoice","description":"Return the projected next-invoice payload for the tenant.","operationId":"getBillEstimate","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillEstimateResponse"}}}}}}},"/backoffice/tenant/{tenant_id}/payg-cap/override":{"post":{"tags":["Backoffice · PAYG (WS-5)"],"summary":"Override a tenant's PAYG spend cap","description":"Set the cap on a tenant's ``tenant_payg_config`` row regardless\nof their self-serve setting.\n\nPreserves ``payg_enabled`` (the toggle is a separate ops action).\nSame F-8 mid-period semantics as the customer-facing path —\nraising is immediate for new queries, lowering below current\nspend blocks new charges.\n\nIf the row doesn't exist yet, this UPSERT creates it with\n``payg_enabled=false`` and the cap from the request — opting the\ntenant into PAYG remains a customer-facing-only action.","operationId":"backofficeOverridePaygCap","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaygCapOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/app__domains__enterprise__billing__presentation__payg_backoffice_routes__BackofficeOverrideAck"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/payg-history":{"get":{"tags":["Backoffice · PAYG (WS-5)"],"summary":"PAYG charge log for a tenant","description":"Admin-only read of the PAYG query log for a tenant.\n\nDate filters are optional. Returns at most 1000 rows (latest first)\nto keep response sizes bounded — the backoffice UI paginates by\ndate window if needed.","operationId":"backofficePaygHistory","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaygHistoryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/cancel":{"post":{"tags":["Billing · Closure (WS-8)"],"summary":"Cancel the tenant's subscription","description":"Owner only — cancellation is the single most consequential\nmoney-affecting action.\n\nReturns 200 with the ``CancelResponse`` shape on success.\nReturns 409 with the structured ``blockers`` array on\ncancellation_blocked. Returns 409 with ``no_active_subscription``\nwhen the tenant has no active plan.\n\nF-20 (cancellation at billing-period boundary): the lifecycle\nstays ``cancelled`` until ``current_period_end``; the existing\ndunning sweep handles the cancelled → suspended transition at\nthe boundary. ``effective_at`` in the response is the actual\nperiod_end so the frontend can show the right \"you'll lose\naccess on [date]\" message.\n\nF-3 / idempotency: a second call after success returns\n``idempotent_replay=True`` without re-issuing pack refunds or\nre-calling Stripe.","operationId":"cancelSubscription","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/refund-request":{"post":{"tags":["Billing · Closure (WS-8)"],"summary":"Request a 30-day money-back refund","description":"Owner only — refunds reduce settled revenue.\n\nReturns 200 with the ``RefundResponse`` shape on success. Returns\n409 with ``refund_outside_window`` / ``refund_no_charge_found`` /\n``refund_not_applicable_on_monthly`` per Phase A spec §1951.\n\nThe Stripe refund is issued synchronously; the eventual\n``refund-processed`` email follows asynchronously via the\n``ChargeRefundedEvent`` listener (which sees the webhook Stripe\nfires in response to our refund creation).\n\nF-3 (refund issued twice): the F-2 op-log + Stripe-side\nidempotency key collapse a retried call to the prior result.","operationId":"requestRefund","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/cancel-scheduled-change":{"post":{"tags":["Billing · Closure (WS-8)"],"summary":"Void a queued scheduled plan change","description":"Owner or Admin. Voids any queued row in\n``tenant_scheduled_plan_change`` for the tenant.\n\nIdempotent: returns 200 with ``voided=False`` when nothing was\nqueued. Graceful when WS-14's table doesn't exist yet — the\nresponse carries ``ws14_table_missing=True`` so the frontend\ncan show \"this feature isn't available yet\" or just silently\nsucceed.","operationId":"cancelScheduledChange","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelScheduledChangeResponse"}}}}}}},"/api/billing/seats/status":{"get":{"tags":["Billing · Plan Transitions (WS-14)"],"summary":"Current subscription seat capacity","description":"Return seat capacity for billing UI seat-management surfaces.","operationId":"getBillingSeatStatus","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatStatusResponse"}}}}}}},"/api/billing/plan/change":{"post":{"tags":["Billing · Plan Transitions (WS-14)"],"summary":"Change plan tier (and optionally seat count + corpus tier)","description":"Owner-or-Admin only. Routes through the WS-14 PlanTransitionHandler.\n\nDecision tree applied:\n  * upgrade / downgrade / same-tier seat resize\n  * immediate / scheduled per OD-14-D flap damper\n  * Free → paid requires hosted Stripe Checkout\n  * downgrade-to-Free pack refund + auto-renew disable (OD-14-C)\n  * corpus reconciliation\n  * Stripe Subscription.modify with F-2 op-log","operationId":"changePlan","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanChangeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/seats/change":{"post":{"tags":["Billing · Plan Transitions (WS-14)"],"summary":"Resize seat count on the current plan","description":"Owner-or-Admin only. F-15 enforced (cannot drop below 1 seat).\n\nSeat REDUCTIONS schedule at period boundary by default (F-4 —\ncustomer keeps the seats they paid for through the current\nperiod). Seat INCREASES apply immediately with proration.","operationId":"changeSeats","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SeatChangeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/transitions/preview":{"get":{"tags":["Billing · Plan Transitions (WS-14)"],"summary":"Forecast the effect of a proposed plan transition","description":"Read-only preview. Stripe consulted via upcoming-invoice (F-12).\n\nAny-member can call (Members are entitled to see what an upgrade\nwould cost their team). API-key readable for backoffice tools.","operationId":"getTransitionPreview","parameters":[{"name":"target_plan_tier","in":"query","required":true,"schema":{"type":"string","pattern":"^(free|team|growth|enterprise)$","title":"Target Plan Tier"}},{"name":"target_seat_count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Target Seat Count"}},{"name":"target_corpus_tier_option_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Target Corpus Tier Option Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TransitionPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/checkout-session":{"post":{"tags":["Billing · Checkout"],"summary":"Create a Stripe Checkout session for a new subscription","description":"Returns a hosted Stripe Checkout URL for the Free-tier tenant to subscribe to Team or Growth. Owner-or-Admin only. Existing-subscription tenants must use Customer Portal (/v1/enterprise/billing/setup-payment) instead.","operationId":"create_checkout_session_api_billing_checkout_session_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/marketing-content":{"get":{"tags":["Billing · Plans"],"summary":"Get the marketing-site pricing content (taglines, bullets, comparison).","description":"Proxy the marketing site's pricing JSON. Editorial copy = single source of truth.","operationId":"get_marketing_content_route_api_billing_marketing_content_get","responses":{"200":{"description":"Marketing-site pricing JSON, fetched from Settings.marketing_pricing_content_url with a 5-minute in-process cache. When the URL is unset or fetch fails, returns available=False; modal falls back to DB-derived data. Editorial copy is non-critical — upgrade still works.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MarketingPricingContentSchema"}}}}}}},"/api/billing/plans":{"get":{"tags":["Billing · Plans"],"summary":"Get the plan catalog + region-adjusted pricing.","description":"Return plan catalog + comparison matrix for the tenant's region.","operationId":"get_plans_api_billing_plans_get","responses":{"200":{"description":"Plan tiers + comparison matrix. Pricing is region-adjusted (seat-price multiplier from region_multipliers); display is always USD.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingPlansResponseSchema"}}}}}}},"/backoffice/tenant/{tenant_id}/corpus-tier/override":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Override Corpus Tier","operationId":"post_override_corpus_tier_backoffice_tenant__tenant_id__corpus_tier_override_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CorpusTierOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck"},{"$ref":"#/components/schemas/BackofficeOverrideDryRun"}],"title":"Response Post Override Corpus Tier Backoffice Tenant  Tenant Id  Corpus Tier Override Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/kt-pool/override":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Override Kt Pool","description":"Lock-additive — see dispatch-log OCI-5.\n\nSets / clears ``tenant_plan.override_daily_kt_pool_per_seat``. The\nview ``effective_tenant_plan`` resolves\n``daily_kt_pool_per_seat = COALESCE(override, plan default)`` so\nWS-3's ``check_query_allowed`` sees the override on the very next\nquery. The integration test\n``test_kt_pool_override_flips_check_query_allowed`` proves this.","operationId":"post_override_kt_pool_backoffice_tenant__tenant_id__kt_pool_override_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/KtPoolOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck"},{"$ref":"#/components/schemas/BackofficeOverrideDryRun"}],"title":"Response Post Override Kt Pool Backoffice Tenant  Tenant Id  Kt Pool Override Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/daily-cap-history":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Tenant Daily Cap History","operationId":"get_tenant_daily_cap_history_backoffice_tenant__tenant_id__daily_cap_history_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Tenant Daily Cap History Backoffice Tenant  Tenant Id  Daily Cap History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/grandfathering/snapshot":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Tenant Grandfathering Snapshot","description":"Lock-additive — see dispatch-log OCI-5.\n\nReturns the tenant's grandfathering window + snapshot JSONB so the\noperator can review before extending or migrating.","operationId":"get_tenant_grandfathering_snapshot_backoffice_tenant__tenant_id__grandfathering_snapshot_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Tenant Grandfathering Snapshot Backoffice Tenant  Tenant Id  Grandfathering Snapshot Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/reports/heavy-corpus-usage":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Report Heavy Corpus Usage Route","operationId":"report_heavy_corpus_usage_route_backoffice_reports_heavy_corpus_usage_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"used_percent_floor","in":"query","required":false,"schema":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","default":"80","title":"Used Percent Floor"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":100,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Heavy Corpus Usage Route Backoffice Reports Heavy Corpus Usage Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/reports/payg-spend-cap-hit":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Report Payg Spend Cap Hit Route","operationId":"report_payg_spend_cap_hit_route_backoffice_reports_payg_spend_cap_hit_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Payg Spend Cap Hit Route Backoffice Reports Payg Spend Cap Hit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/reports/free-conversion-funnel":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Report Free Conversion Route","operationId":"report_free_conversion_route_backoffice_reports_free_conversion_funnel_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Free Conversion Route Backoffice Reports Free Conversion Funnel Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/reports/bundle-purchase-trends":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Report Bundle Trends Route","operationId":"report_bundle_trends_route_backoffice_reports_bundle_purchase_trends_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"weeks_back","in":"query","required":false,"schema":{"type":"integer","maximum":52,"minimum":1,"default":12,"title":"Weeks Back"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Bundle Trends Route Backoffice Reports Bundle Purchase Trends Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/reports/grandfathered-migration-status":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Report Grandfathered Route","operationId":"report_grandfathered_route_backoffice_reports_grandfathered_migration_status_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"default":500,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Report Grandfathered Route Backoffice Reports Grandfathered Migration Status Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/grandfathering/extend":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Extend Grandfathering","operationId":"post_extend_grandfathering_backoffice_tenant__tenant_id__grandfathering_extend_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrandfatheringExtendRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck"},{"$ref":"#/components/schemas/BackofficeOverrideDryRun"}],"title":"Response Post Extend Grandfathering Backoffice Tenant  Tenant Id  Grandfathering Extend Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/grandfathering/migrate-now":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Migrate Grandfathered Now","operationId":"post_migrate_grandfathered_now_backoffice_tenant__tenant_id__grandfathering_migrate_now_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrandfatheringMigrateRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck"},{"$ref":"#/components/schemas/BackofficeOverrideDryRun"}],"title":"Response Post Migrate Grandfathered Now Backoffice Tenant  Tenant Id  Grandfathering Migrate Now Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/provision-enterprise":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Provision Enterprise","description":"Lock-additive (F-16) — see dispatch-log OCI-5.\n\nBelow 100 seats and ``force=false`` returns 422 with\n``error_code='enterprise_seat_minimum_not_met'`` so the operator\nsees the explicit guard rather than a silent acceptance. The\n``force=true`` query parameter is the escape hatch (e.g. a sales-\napproved 50-seat enterprise pilot); the audit row records the\nforced flag in ``response.side_effects``.","operationId":"post_provision_enterprise_backoffice_tenant__tenant_id__provision_enterprise_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"dry_run","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Dry Run"}},{"name":"force","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Force"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProvisionEnterpriseRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck"},{"$ref":"#/components/schemas/BackofficeOverrideDryRun"}],"title":"Response Post Provision Enterprise Backoffice Tenant  Tenant Id  Provision Enterprise Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/feature-gate":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Tenant Feature Gate","description":"Lock-additive (T16a, OCI-7) — admin-key surface over FeatureGate.\n\nThe customer-facing ``GET /api/billing/features`` (WS-15) resolves\ntenant from the SSO session; the backoffice needs per-tenant\ninspection. This endpoint wraps the same\n``FeatureGate(tenant_id, db).resolve()`` for a Quelvio operator,\nreturning the Lock-defined ``BillingFeaturesResponse`` shape\nverbatim so the backoffice capability grid never has to derive\ntier policy frontend-side.","operationId":"get_tenant_feature_gate_backoffice_tenant__tenant_id__feature_gate_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BillingFeaturesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/identity/scim/summary":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Tenant Scim Summary","description":"SCIM activation summary for one tenant.\n\nReturns:\n  * ``active_token_count``       — non-revoked SCIM tokens.\n  * ``provisioned_user_count``   — active SCIM-provisioned users\n                                   (mapping × tenant_member where\n                                   ``deactivated_at IS NULL``).\n  * ``last_scim_activity_at``    — max ``last_used_at`` across\n                                   non-revoked tokens; null when\n                                   no token has been used yet.\n\nMirrors the spec in the identity tier-gating audit §6 item 5.","operationId":"get_tenant_scim_summary_backoffice_tenant__tenant_id__identity_scim_summary_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Tenant Scim Summary Backoffice Tenant  Tenant Id  Identity Scim Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/identity":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Tenant Identity","description":"Lock-additive — per-tenant SAML + SCIM activation surface.\n\nPowers the backoffice Identity tab introduced by the v0.9 identity\ntier-gating audit (``docs/audits/2026-05-23-identity-tier-gating-\naudit.md`` §4). The CapabilityGrid endpoint above answers \"does the\nplan grant the capability?\"; THIS endpoint answers the orthogonal\nquestion \"has the tenant activated the capability?\". Operators need\nboth to triage support tickets (\"their plan says SAML on but the\nSAML page is empty\" / \"we downgraded them yesterday but their IdP\nis still routing\").\n\nSCIM half populates real counters from the SCIM tables now that\nthe SCIM workstream has merged.","operationId":"get_tenant_identity_backoffice_tenant__tenant_id__identity_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IdentityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/health/billing":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Billing Health","description":"Lock-additive (F-22) — see dispatch-log OCI-5.\n\nThe launch-day rollup. ONE screen tells the operator whether the\nmoney machine is working. Numbers are aggregates across ALL tenants\n— no per-tenant detail here; per-tenant drilldowns are the\ninspection endpoints above.","operationId":"get_billing_health_backoffice_health_billing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Billing Health Backoffice Health Billing Get"}}}}},"security":[{"APIKeyHeader":[]}]}},"/backoffice/tenant/{tenant_id}/plan/transitions/preview":{"get":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Get Backoffice Plan Transition Preview","description":"Lock-additive (T16b, OCI-9) — backoffice forecast of a plan transition.\n\nWraps WS-14's ``preview_plan_transition`` query function. Read-only:\nno Stripe writes, no DB mutations, no events. The response zeros\nout the flap-damper fields because the apply path bypasses the\ndamper for ``source='backoffice'``; surfacing the verdict here\nwould mislead the operator about what their Apply click will do.\n\nAuth: ``X-Admin-Key``. Customer-facing ``GET /api/billing/transitions/\npreview`` is unaffected.","operationId":"get_backoffice_plan_transition_preview_backoffice_tenant__tenant_id__plan_transitions_preview_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}},{"name":"target_plan_tier","in":"query","required":true,"schema":{"type":"string","pattern":"^(free|team|growth|enterprise)$","title":"Target Plan Tier"}},{"name":"target_seat_count","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":0},{"type":"null"}],"title":"Target Seat Count"}},{"name":"target_corpus_tier_option_id","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Target Corpus Tier Option Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackofficePlanTransitionPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/tenant/{tenant_id}/plan/override":{"post":{"tags":["v0.9 Backoffice (WS-12)","v0.9 Backoffice (WS-12)"],"summary":"Post Backoffice Plan Override","description":"Lock-additive (T16b, OCI-9) — backoffice apply of a plan transition.\n\nWraps ``PlanTransitionHandler.request_change`` with\n``source='backoffice'``. Per WS-14's handler invariants:\n  * Bypasses the ``plan_change_not_self_serve`` gate so the operator\n    can move a tenant to Enterprise (or revert from Enterprise to\n    a self-serve tier) without needing the tenant to do it.\n  * Bypasses the F-19 flap damper — backoffice overrides do not\n    count toward the 24h-2-changes self-serve guard.\n  * Emits ``TenantPlanChangedEvent`` with\n    ``trigger='backoffice_override'`` (WS-7 routes this to the\n    `plan-upgraded` lifecycle email per the WS-14 listener wiring).\n  * The corpus-fit (OD-14-B) and seat-floor (F-15) gates DO apply —\n    backoffice cannot data-corrupt by downgrading into a corpus\n    ceiling the tenant doesn't fit in, or by removing the last\n    seat of a paid plan.\n\nTwo audit trails written on every successful apply (NOT on errors):\n  1. ``plan_change_audit_log`` — WS-14's billing-ops audit;\n     ``audit_log_id`` field on the response.\n  2. ``backoffice_audit_log`` — T16a's backoffice-ops audit; row id\n     on the response as ``backoffice_audit_log_id`` so Screen 1's\n     audit-log reader (which reads the merged WS-12 +\n     backoffice_audit + state-machine stream) shows the entry\n     immediately.\n\nAuth: ``X-Admin-Key``. Customer-facing ``POST /api/billing/plan/change``\nis unaffected.","operationId":"post_backoffice_plan_override_backoffice_tenant__tenant_id__plan_override_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackofficePlanOverrideRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BackofficePlanChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/pat-audit/recent":{"get":{"tags":["Backoffice · PAT Audit (B+ Phase 6-A)"],"summary":"Recent PAT/ent-key queries (audit feed)","description":"Returns the most recent ``query_logs`` rows with a non-NULL\n``api_key_id`` — i.e. queries authenticated via PAT or enterprise\nAPI key.\n\nThe privacy-preserving ``query_hash_prefix`` (first 16 chars of the\nSHA-256 hash) plus ``taxonomy_domain`` give the operator enough\nsignal to spot abuse patterns without exposing prompt text.","operationId":"backofficePatAuditRecent","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":336,"minimum":1,"description":"Window size in hours. Default 24, cap 336 (14 days).","default":24,"title":"Hours"},"description":"Window size in hours. Default 24, cap 336 (14 days)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Row cap. Capped at 500.","default":200,"title":"Limit"},"description":"Row cap. Capped at 500."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecentQueriesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/pat-audit/per-key/{api_key_id}":{"get":{"tags":["Backoffice · PAT Audit (B+ Phase 6-A)"],"summary":"Per-key PAT/ent-key activity + heuristic summary","description":"Resolve ``api_key_id`` to its credential row (PAT or ent-key),\nthen summarise the window's activity + run the heuristics package.\n\nReturns 404 if the id is unknown to BOTH ``personal_access_tokens``\nand ``enterprise_api_keys``. A row that exists in the credential\ntable but has no activity in the window returns 200 with\n``query_count=0`` and empty signals — distinguishing \"key not\nfound\" from \"key exists but quiet\" is what the operator needs.","operationId":"backofficePatAuditPerKey","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"api_key_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Api Key Id"}},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","maximum":720,"minimum":1,"description":"Window size in hours. Default 24, cap 720 (30 days).","default":24,"title":"Hours"},"description":"Window size in hours. Default 24, cap 720 (30 days)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PerKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/backoffice/pat-audit/suspicious":{"get":{"tags":["Backoffice · PAT Audit (B+ Phase 6-A)"],"summary":"Keys flagged by ≥1 heuristic in the window","description":"Iterate every key with activity since ``since``, run the\nheuristics, return only those with ≥1 fired signal.\n\nO(N × M) where N is the number of active keys and M is the average\nactivity slice size — fine at backoffice scale. A future tighten\nwould precompute the per-key counters in a materialised view, but\nkeep this simple until the operator asks.","operationId":"backofficePatAuditSuspicious","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Window start (ISO8601, timezone-aware). Defaults to 24h ago. Must be in the past.","title":"Since"},"description":"Window start (ISO8601, timezone-aware). Defaults to 24h ago. Must be in the past."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuspiciousKeysResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/confluence/search":{"post":{"tags":["Integrations · Confluence App","Integrations · Confluence App"],"summary":"Confluence Search","description":"Execute a Quelvio search on behalf of a Confluence user.\n\nRequest body (JSON):\n    {\n      \"query\": \"...\",                    # str, 1..400 chars\n      \"exclude_source\": \"confluence\"     # optional — Q13 filter\n                                         # for the byline panel\n    }\n\nResponse (200, DTO per Q10):\n    {\n      \"ok\": true,\n      \"synthesis\": null,                 # v0.5.14 is structured_standard only\n      \"sources\": [\n        {\"title\": \"...\", \"excerpt\": \"...\",\n         \"url\": \"...\", \"source_type\": \"...\"},\n        ...\n      ],\n      \"coverage\": \"expert\" | \"partial\" | \"none\",\n      \"deep_link\": \"https://enterprise.quelvio.com/search?q=...\",\n      \"unrecognised_user\": false,\n      \"rate_limited\": null\n    }\n\nKill-switch / unknown-site / paused / revoked all return 200 with\n``ok=true`` and empty sources — Forge UI renders a neutral state.\nAuth failures return 401/403 per ``_fit_error_to_http``.","operationId":"confluence_search_v1_integrations_confluence_search_post","requestBody":{"content":{"application/json":{"schema":{"type":"object","title":"Payload"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Confluence Search V1 Integrations Confluence Search Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/integrations/confluence/link-tenant":{"post":{"tags":["Integrations · Confluence App","Integrations · Confluence App"],"summary":"Link Tenant","description":"Admin install / link handler (FIT-only auth).\n\nIdentifies the admin via the FIT's ``accountId``, looks up their\nemail via ``asApp()`` against Atlassian's user-email endpoint, and\nresolves the email to a ``TenantMember`` with role=admin. Upserts\nthe ``ConfluenceAppInstall`` row keyed on ``cloud_id``.\n\nAmbiguous admins (same email on multiple Quelvio tenants) receive\n409 — they must use the dashboard Sources page to explicitly pick\nwhich tenant this Confluence site links to.","operationId":"link_tenant_v1_integrations_confluence_link_tenant_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Link Tenant V1 Integrations Confluence Link Tenant Post"}}}}}}},"/v1/integrations/confluence/installs":{"get":{"tags":["Integrations · Confluence App","Integrations · Confluence App"],"summary":"List Installs","description":"List the tenant's active Confluence App installs. Sources page payload.","operationId":"list_installs_v1_integrations_confluence_installs_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Installs V1 Integrations Confluence Installs Get"}}}}}}},"/v1/integrations/confluence/install/{install_id}":{"delete":{"tags":["Integrations · Confluence App","Integrations · Confluence App"],"summary":"Disconnect Install","description":"Soft-revoke a Confluence App install. Admin-only.","operationId":"disconnect_install_v1_integrations_confluence_install__install_id__delete","parameters":[{"name":"install_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Install Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/query":{"post":{"tags":["Enterprise"],"summary":"Enterprise Query","description":"Search the tenant's Qdrant collection using the existing retrieval pipeline.\n\nv0.8.8 SSO foundation: switched from ``get_enterprise_auth`` to\n``get_per_employee_auth`` so MCP traffic now resolves to the actual\nquerying employee. The downstream ``PermissionContext`` filter at\n``hybrid_search._build_permission_filter`` reads\n``employee.email``/``role``/``groups`` — those values now reflect\nthe human, not the API-key minter, closing the v0.7.x audit's #5\nP1 finding.","operationId":"enterprise_query_v1_enterprise_query_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnterpriseQueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnterpriseQueryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/query/stream":{"post":{"tags":["Enterprise"],"summary":"Enterprise Query Stream","description":"SSE streaming variant of the enterprise query endpoint.\n\nv0.5.15 W8 — converted from GET to POST. The previous GET shape\naccepted only ``q`` + ``mode`` + ``limit`` querystring params, which\nforced the frontend to cram conversation history into ``q`` (the\n\"kludge\"). The new POST accepts the full ``EnterpriseQueryRequest``\nbody including ``conversation_id`` and ``prior_turns`` so the\nbackend owns conversation context end-to-end.\n\nYields a sequence of events that lets the dashboard render progress\nincrementally instead of waiting for the full ~15s synthesis call:\n\n  ``status``             — high-level stage transitions\n  ``sources``            — ranked source list (after retrieval, ~500ms)\n  ``status``             — synthesis stage entered\n  ``synthesis_chunk``    — token deltas from the LLM stream\n  ``grounding_verified`` — MiniCheck verification (synthesis modes only)\n  ``done``               — final query_id, latency, tokens consumed,\n                          thread_id (W21 — for follow-up persistence)\n  ``error``              — emitted if any stage fails\n\nAuth, billing, and tenant isolation match POST /v1/enterprise/query\nexactly. Tokens are debited up front so a cancelled stream still consumes\nthem — same policy as the non-streaming endpoint.","operationId":"enterprise_query_stream_v1_enterprise_query_stream_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EnterpriseQueryRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me":{"get":{"tags":["Enterprise"],"summary":"Get Me","description":"Return the current user's profile and onboarding status.","operationId":"get_me_v1_enterprise_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Me V1 Enterprise Me Get"}}}}}},"put":{"tags":["Enterprise"],"summary":"Update Me","description":"Update the current user's profile (department, display name, title).","operationId":"update_me_v1_enterprise_me_put","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Me V1 Enterprise Me Put"}}}}}}},"/v1/enterprise/me/onboarding/complete":{"post":{"tags":["Enterprise"],"summary":"Complete Onboarding","description":"Mark onboarding finished for the current member.\n\nBody (optional): ``{\"step\": \"welcome\" | \"workspace\" | \"invite\" |\n\"connect\"}`` — the step from which the user finished. Stored on\n``onboarding_step`` for analytics; the gate check only looks at\n``onboarding_completed_at``.","operationId":"complete_onboarding_v1_enterprise_me_onboarding_complete_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Complete Onboarding V1 Enterprise Me Onboarding Complete Post"}}}}}}},"/v1/enterprise/connectors/available":{"get":{"tags":["Enterprise"],"summary":"List Available Connectors","description":"List connector types the workspace can configure.\n\nDecoupled from the implementation files under\n``infrastructure/connectors/`` so adding a new card here doesn't\nrisk touching connector-runtime code (Workstream A territory).","operationId":"list_available_connectors_v1_enterprise_connectors_available_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Available Connectors V1 Enterprise Connectors Available Get"}}}}}}},"/v1/enterprise/admin/members/{member_id}/role":{"put":{"tags":["Enterprise"],"summary":"Update Member Role","description":"Change a member's role. Owner / admin caller.\n\nInvariants enforced (v0.8.8):\n- Caller must be owner or admin (route gate covers this; explicit\n  check belt-and-braces against API-key callers slipping past).\n- Target role must be 'admin' or 'member'. Owner cannot be granted\n  via this endpoint — use POST /admin/members/{id}/transfer-ownership.\n- The current owner row cannot be altered through this endpoint.\n  An admin caller demoting the owner to admin would dissolve the\n  \"exactly one owner per tenant\" invariant; an owner caller\n  demoting themselves bypasses the transfer flow's atomic role swap.\n  Either way: 403 with \"Use Transfer Ownership instead.\"\n- Idempotent — same-role assignment returns 200 with no audit row.","operationId":"update_member_role_v1_enterprise_admin_members__member_id__role_put","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Update Member Role V1 Enterprise Admin Members  Member Id  Role Put"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/members/{target_id}/transfer-ownership":{"post":{"tags":["Enterprise"],"summary":"Transfer Ownership","description":"Transfer workspace ownership to an existing tenant member.\n\nOwner-only. The current owner becomes admin; the target becomes the\nnew owner. Atomic in a single transaction so the partial unique\nindex ``tenant_members_one_owner_per_tenant`` (alembic v088a001) is\nsatisfied at every point: there is always exactly one owner row.\n\nBody: ``{\"confirmation\": \"<workspace name>\"}`` (case-insensitive,\nwhitespace trimmed). Locked operator decision #4 — Notion-style\ntype-the-workspace-name confirmation, harder to misclick than a\nbare 'yes' string.\n\nErrors:\n  403 — caller is admin (not owner): \"Only the workspace owner...\"\n  403 — target is the caller themselves: \"Choose a different member...\"\n  404 — target_id is not an active member of the tenant\n  409 — target is currently 'owner' (defensive; the unique index\n        would also reject any path that tries to make two owners)\n  422 — confirmation does not match the workspace name","operationId":"transfer_ownership_v1_enterprise_admin_members__target_id__transfer_ownership_post","parameters":[{"name":"target_id","in":"path","required":true,"schema":{"type":"string","title":"Target Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Transfer Ownership V1 Enterprise Admin Members  Target Id  Transfer Ownership Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/members":{"get":{"tags":["Enterprise"],"summary":"List Members","description":"List all members of the current tenant.\n\nAny member can see who shares their workspace. Role mutations still\nrequire admin (PUT /admin/members/{id}/role, DELETE /members/{id}).","operationId":"list_members_v1_enterprise_members_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Members V1 Enterprise Members Get"}}}}}}},"/v1/enterprise/members/{member_id}":{"delete":{"tags":["Enterprise"],"summary":"Remove Member","description":"Remove a member from the tenant. Owner / admin only.\n\nInvariants (v0.8.8):\n- Owner row cannot be deleted via this endpoint (OAC-001 — single\n  owner per workspace; ownership is transferred, not deleted). The\n  message points the caller at the transfer flow.\n- Admin row CAN be deleted directly. Prior to v0.8.8 this returned\n  403 (\"Demote to manager or member first\") — locked operator\n  decision dropped that gate. The owner-rejection above is the only\n  invariant that matters; the admin friction added cost without\n  security value (the caller is owner or admin per the route gate,\n  so they're authorized to remove other admins).\n- Self-removal: an admin removing their own row is allowed (gate\n  catches owner self-removal because the owner branch fires first).","operationId":"remove_member_v1_enterprise_members__member_id__delete","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/account/me":{"delete":{"tags":["Enterprise"],"summary":"Delete Own Account","description":"Delete the caller's tenant_members rows across every tenant.\n\nv0.8.8 introduces this endpoint to enforce invariant 7: account\ndeletion is blocked while the user owns any tenant — the user\nmust transfer ownership in each owned tenant first. The endpoint\ncleans up Quelvio-side membership data only; it does NOT delete\nthe caller's Clerk user record (Clerk owns the user identity).\n\nBehavior:\n  * Fetches every ``tenant_members`` row matching the caller's\n    email across all tenants. (Today the product is single-tenant\n    per email, but the data model permits multi-tenant; this\n    endpoint is forward-compatible.)\n  * If any row has ``role='owner'`` → 409 with the list of owning\n    tenants. Frontend renders one transfer flow per blocking tenant.\n  * Otherwise → DELETE every membership row, audit-log the event,\n    return 204. The caller's session token continues to work until\n    it expires; the next request will 401 because the membership\n    rows are gone.\n\nLocked operator decision #3: Quelvio-side cleanup only. The Clerk\nwebhook integration (so deleting the Clerk user also fires this\nendpoint server-to-server) is deferred to v0.8.9+.","operationId":"delete_own_account_v1_enterprise_account_me_delete","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/enterprise/library/{piece_id}":{"get":{"tags":["Enterprise"],"summary":"Get Library Piece","description":"Return a single library piece's metadata for the detail panel.\n\nB+ Phase 1C — per design §3.6 row 3, the per-piece detail endpoint\nmust gate on ``_employee_allowed_on_piece`` after tenant scoping so\na non-grantee cannot bypass the list filter by hitting the piece\ndirectly via its UUID. Returns 404 (NOT 403) on the permission\nmiss to prevent an IDOR probe from distinguishing\nrestricted-but-existent pieces from non-existent UUIDs — same\nrationale as the media endpoint at lines ~4486 onward.\n\nPhase 1 compatibility shim: ``allow_null_legacy=True`` keeps pre-B+\nNULL-permission rows visible to every tenant member. Phase 5 flips\nthe default to False once the Phase 4 backfill assigns explicit\nACLs to every existing row.\n\nReturns 404 when:\n  * the piece UUID is malformed,\n  * the piece doesn't belong to the caller's tenant,\n  * the piece was soft-deleted (``user_deleted_at``) or removed,\n  * the caller is not in the piece's ``permission_emails`` list.","operationId":"get_library_piece_v1_enterprise_library__piece_id__get","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Library Piece V1 Enterprise Library  Piece Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Enterprise"],"summary":"Remove Library Item","description":"Soft-delete a content piece from the library (v0.4.17 semantics).\n\nSets ``user_deleted_at = now()``, ``user_deleted_by = member.id``,\nand ``enrichment_status = \"user_deleted\"``. The piece is hidden\nfrom the default library view and from search but its raw S3 object,\nDB rows, and Qdrant points stay for 7 days so a Resync can restore it\nwithout re-running the full enrichment pipeline. The\n``user_deleted_ttl`` nightly job hard-deletes pieces whose\n``user_deleted_at`` is older than 7 days. v0.5.15 W15 — the\nretrieval-side filter (``deleted_content_filter``) is what hides\nsoft-deleted pieces during the grace window; without it, search\nleaks deleted content until the TTL fires.\n\nThe legacy ``removed_at`` / ``removed_by`` columns are also set\nso existing reads (connector dashboards, file_count subqueries)\nkeep working unchanged. Future PRs will retire the legacy pair\nonce no readers remain.\n\nPermission rules (v0.8.8): owner / admin only. The endpoint\npreviously gated at ``RequiredRole.MANAGER`` and ran an in-handler\nmatrix that distinguished admin (full tenant), manager (own\ndepartment), and member (own author_email) deletes. The member\nbranch was unreachable in production — ``role_satisfies('member',\n'manager') == False`` blocked members at the gate before the\nhandler ran — and the manager role had 0 production rows. With\nManager removed in v0.8.8 the gate moves to ADMIN and the dead\nsub-branches are removed. Member-self-content-delete, if customer\ndemand surfaces, is a separate product decision.","operationId":"remove_library_item_v1_enterprise_library__piece_id__delete","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Remove Library Item V1 Enterprise Library  Piece Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library/{piece_id}/permissions":{"patch":{"tags":["Enterprise"],"summary":"Patch Library Piece Permissions","description":"Replace a content piece's ``permission_emails`` snapshot (B+ Phase 3B).\n\nRequest body — :class:`PatchPiecePermissionsRequest`::\n\n    {\n      \"permission_emails\": list[str] | \"__public_tenant__\",\n      \"permission_resolution_mode\": str (optional, advisory)\n    }\n\nNote: the legacy ``reason`` field was removed from the request\nbody — operators no longer supply an audit justification on the\nwire. The audit-log column remains nullable; new PATCH writes\ninsert NULL.\n\nThe ``permission_resolution_mode`` field is currently advisory —\nevery successful PATCH stamps ``manual_override`` per design §3.13.\nAccepting the field on the wire keeps the API contract compatible\nwith the future expand of Phase 3B (per-file ACL refresh PATCH,\netc.) without a breaking change.\n\nThe body shape is validated by the shared\n:class:`PatchPiecePermissionsRequest` Pydantic model — same wire\ncontract the Phase 3C bulk PATCH uses for its ``emails`` field,\nsame sentinel-injection guard. The application-layer validator\nhandles the richer per-entry membership / domain checks.\n\nReturns\n-------\n200 with the updated piece's permission snapshot.\n\nErrors\n------\n404\n    Piece does not belong to the caller's tenant or has been\n    soft-deleted. IDOR-safe — same response as GET /library/{id}\n    on a foreign-tenant UUID.\n409\n    Piece is in a connector-managed\n    ``permission_resolution_mode`` (``per_file_acl``,\n    ``workspace_inherited``, ``connector_default``,\n    ``explicit_public``, ``unresolved_acl_skipped``). The\n    operator-stated rule (manage-access redesign) reserves\n    platform-level PATCH for sources whose ACL Quelvio owns\n    (``manual_override`` / ``uploader_only``); connector-managed\n    pieces must be edited at the source platform. The 409 body\n    carries ``connector_type`` + ``source_url`` so the dashboard\n    can deep-link the operator to the right \"Manage at source\"\n    surface.\n422\n    Body shape / validator failure. The detail carries\n    ``error_code`` (machine-parsable) + ``message`` (operator-readable).\n    For the FS-contracts deprecation cycle the legacy ``error`` key\n    is also populated, mirroring ``error_code``.\n403\n    Role check failed (member without admin) or auth source was\n    not dashboard SSO. Handled by the ``require_access`` gate.","operationId":"patch_library_piece_permissions_v1_enterprise_library__piece_id__permissions_patch","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPiecePermissionsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PatchPiecePermissionsResponse"}}}},"404":{"description":"Piece not found or soft-deleted (IDOR-safe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"409":{"description":"Piece is in a connector-managed permission_resolution_mode (per_file_acl / workspace_inherited / connector_default / explicit_public / unresolved_acl_skipped). Access must be edited at the source platform.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"422":{"description":"Body shape / validator failure.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"403":{"description":"Role check failed or auth source not dashboard SSO.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}}}},"delete":{"tags":["Enterprise"],"summary":"Reset Library Piece Permissions","description":"Drop a piece's manual_override and restore the source-resolved ACL.\n\nReturns\n-------\n200 with the post-reset piece snapshot (mirrors the PATCH\nresponse shape, minus the ``manual_override_expires_at`` field).\n\nErrors\n------\n404\n    Piece does not belong to the caller's tenant or has been\n    soft-deleted. IDOR-safe — same response as the PATCH route on\n    a foreign-tenant UUID.\n409\n    Piece is not currently in ``manual_override`` mode (i.e. the\n    connector resolver owns the ACL, or the piece is\n    ``uploader_only`` / NULL legacy). The frontend renders the\n    \"manage at source\" copy.\n403\n    Role check failed or auth source was not dashboard SSO.\n    Handled by ``require_access``.","operationId":"reset_library_piece_permissions_v1_enterprise_library__piece_id__permissions_delete","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResetPiecePermissionsResponse"}}}},"404":{"description":"Piece not found or soft-deleted (IDOR-safe).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"409":{"description":"Piece is not in manual_override mode — no source-resolved ACL to revert to.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"403":{"description":"Role check failed or auth source not dashboard SSO.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library/{piece_id}/permission-history":{"get":{"tags":["Enterprise"],"summary":"Get Library Piece Permission History","description":"Return the ordered ``permission_audit_log`` entries for one piece.\n\nBacks the FE-7 audit-log viewer in the dashboard PermissionDrawer:\nthe operator opens a piece and sees every recorded\n``permission_emails`` mutation (operator overrides, connector\nsyncs, lifecycle sweeps, backfills) with the before/after diff.\n\nResponse shape::\n\n    {\n      \"piece_id\": \"...\",\n      \"total_count\": 12,\n      \"entries\": [\n        {\n          \"id\": \"...\",\n          \"mutated_at\": \"ISO-8601\",\n          \"mutation_source\": \"<closed enum>\",\n          \"mutated_by_member_id\": \"uuid|null\",\n          \"mutated_by_email\": \"string|null\",\n          \"permission_emails_before\": [\"...\"] | null,\n          \"permission_emails_after\": [\"...\"],\n          \"resolution_mode_before\": \"string|null\",\n          \"resolution_mode_after\": \"string\",\n          \"reason\": \"string|null\"\n        },\n        ...\n      ]\n    }\n\nEmpty history (a piece that was uploaded but never touched by any\nmutation source) returns ``\"entries\": []`` with ``total_count: 0``\n— NOT a 404. The 404 path is reserved for cross-tenant / missing\npieces so the dashboard can distinguish \"no history yet\" from\n\"this piece doesn't exist for your tenant\".\n\nErrors\n------\n404\n    Piece UUID is malformed, does not belong to the caller's\n    tenant, or has been hard-removed. IDOR-safe — same response\n    as GET /library/{piece_id} on a foreign-tenant UUID.\n403\n    Role check failed (member without admin) or auth source was\n    not dashboard SSO. Handled by ``require_access``.\n422\n    ``limit`` outside [1, 200]. Handled by FastAPI's Query\n    validator.","operationId":"get_library_piece_permission_history_v1_enterprise_library__piece_id__permission_history_get","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Library Piece Permission History V1 Enterprise Library  Piece Id  Permission History Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library/{piece_id}/resync":{"post":{"tags":["Enterprise"],"summary":"Resync Library Item","description":"Manually queue a content piece for re-enrichment (v0.4.16.12).\n\nSets ``enrichment_due_at = now()`` on the enterprise content piece,\nwhich makes the debounce worker pick it up on its next sweep\n(within ~60s) and route it through the existing re-enrichment\npipeline (Qdrant chunk replacement, authority rescoring, etc.).\n\nThis is the manual counterpart to webhook-triggered debounced\nre-enrichment. The same pipeline runs either way — the only\ndifference is the trigger source.\n\nReturns 404 if the piece does not belong to the caller's tenant\nor has been soft-deleted / removed.","operationId":"resync_library_item_v1_enterprise_library__piece_id__resync_post","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Resync Library Item V1 Enterprise Library  Piece Id  Resync Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library/{piece_id}/ingest-anyway":{"post":{"tags":["Enterprise"],"summary":"Ingest Anyway Library Item","description":"Override a quality-gate failure and re-run the pipeline (v0.6.2).\n\nUsed when the customer disagrees with the gate's \"this looks like\nplaceholder content\" or \"this looks like a credentials file\"\ndecision. The override applies to the QUALITY GATE ONLY — secret\nredaction is non-negotiable and runs again on the next enrichment\npass regardless. Any secrets detected on re-extraction stay\nredacted; the customer cannot override that behaviour.\n\nBehavior:\n    * 404 if the piece does not belong to the caller's tenant or has\n      been soft-deleted / removed (matches resync's failure shape —\n      deliberately a 404 for cross-tenant to avoid leaking existence\n      via 403).\n    * 409 if the piece is not in ``awaiting_content`` with a\n      quality-gate reason. The override is meaningless on indexed,\n      processing, or extraction-failed pieces — clients should call\n      ``/resync`` for those instead.\n    * 200 with ``{\"piece_id\", \"status\": \"queued\"}`` on success. The\n      piece's ``gate_decision_metadata`` is stamped with\n      ``override_at`` / ``override_actor`` / ``override_reason``;\n      ``enrichment_due_at`` is set to ``now()`` so the debounce\n      worker (~60s sweep) re-runs the pipeline. The gate sees the\n      override marker on the next pass and short-circuits to\n      ``pass`` without re-evaluating the dictionary / language\n      signals.\n\nAudit log: emits ``ingest_anyway_override`` against ``audit_log``\nwith the previous fail reason and the redaction count at override\ntime.","operationId":"ingest_anyway_library_item_v1_enterprise_library__piece_id__ingest_anyway_post","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Piece Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/IngestAnywayRequest"},{"type":"null"}],"title":"Body"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/IngestAnywayResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content/{content_piece_id}/media":{"get":{"tags":["Enterprise"],"summary":"Get Content Media Url","description":"Return a presigned S3 URL for an audio or video content piece.\n\nThe ``content_piece_id`` is the ``EnterpriseContentPiece.id`` as exposed\nin search responses — we look up the enterprise row by primary key\nand enforce tenant isolation in the same query.\n\nReturns 404 when the piece doesn't belong to the caller's tenant, is\nneither audio nor video, OR the caller is not in its ``permission_emails``\nlist. 404 instead of 403 on the permission check prevents an IDOR probe\nfrom distinguishing \"doesn't exist\" from \"exists but you can't see it.\"\n\nAccepts ``audio/*`` (B9) and ``video/*`` (B10a) mime types. Suspended\nmembers / suspended tenants are refused upstream by\n:func:`require_access`.","operationId":"get_content_media_url_v1_enterprise_content__content_piece_id__media_get","parameters":[{"name":"content_piece_id","in":"path","required":true,"schema":{"type":"string","title":"Content Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Content Media Url V1 Enterprise Content  Content Piece Id  Media Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors":{"get":{"tags":["Enterprise"],"summary":"List Connectors","description":"List connected data sources for the tenant.\n\n``file_count`` is computed dynamically via a grouped COUNT on\n``enterprise_content_pieces`` rather than read from the denormalized\n``EnterpriseConnector.file_count`` column (v0.4.11). The column is\nno longer maintained — the dynamic count is authoritative and\nself-corrects any historical drift on the next page load.","operationId":"list_connectors_v1_enterprise_connectors_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorListResponse"}}}}}}},"/v1/enterprise/connectors/gdrive/auth":{"get":{"tags":["Enterprise"],"summary":"Gdrive Auth","description":"Return the Google OAuth consent URL for Drive integration.\n\nGenerates a CSRF token stored in Redis and passes tenant_id + csrf\nin the OAuth state parameter so the callback (which is public) can\nidentify the tenant without requiring auth headers.","operationId":"gdrive_auth_v1_enterprise_connectors_gdrive_auth_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GDriveAuthUrlResponse"}}}}}}},"/v1/enterprise/connectors/gdrive/callback":{"get":{"tags":["Enterprise"],"summary":"Gdrive Callback","description":"Exchange Google OAuth code for tokens and create connector record.\n\nThis endpoint is PUBLIC (no auth dependency) because Google redirects\nthe browser here — there's no way to pass auth headers. Instead,\ntenant_id is carried in the OAuth state parameter and validated via\na CSRF token stored in Redis during the /gdrive/auth step.","operationId":"gdrive_callback_v1_enterprise_connectors_gdrive_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Google","title":"Code"},"description":"OAuth authorization code from Google"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Google (e.g. access_denied, invalid_scope)","title":"Error"},"description":"OAuth error code from Google (e.g. access_denied, invalid_scope)"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Google","title":"Error Description"},"description":"Human-readable error description from Google"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}/pause":{"patch":{"tags":["Enterprise"],"summary":"Pause Source","description":"Pause content sync for a source.\n\nAuthorisation: admin / manager always; member only when\n``tenant.allow_members_connect`` is True AND the connector's\n``created_by_id`` matches the caller's ``member_id``.\nPermission-update webhooks continue to run — this ONLY gates\nnew-content ingestion.","operationId":"pause_source_v1_enterprise_sources__connector_id__pause_patch","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceStateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}/resume":{"patch":{"tags":["Enterprise"],"summary":"Resume Source","description":"Resume a paused source.","operationId":"resume_source_v1_enterprise_sources__connector_id__resume_patch","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceStateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}/disconnect-preview":{"get":{"tags":["Enterprise"],"summary":"Disconnect Preview","description":"Return file/piece counts to show in the disconnect confirmation.","operationId":"disconnect_preview_v1_enterprise_sources__connector_id__disconnect_preview_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DisconnectPreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}":{"delete":{"tags":["Enterprise"],"summary":"Disconnect Source","description":"Disconnect a source. Cascades Qdrant + SQL synchronously; queues\nOAuth revocation asynchronously via the events queue.","operationId":"disconnect_source_v1_enterprise_sources__connector_id__delete","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}/scope-preview":{"get":{"tags":["Enterprise"],"summary":"Scope Preview","description":"Return how many pieces would be deleted by removing the given scope keys.","operationId":"scope_preview_v1_enterprise_sources__connector_id__scope_preview_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}},{"name":"remove_keys","in":"query","required":false,"schema":{"type":"array","items":{"type":"string"},"title":"Remove Keys"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopePreviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{connector_id}/scope":{"patch":{"tags":["Enterprise"],"summary":"Change Scope","description":"Update a connector's scope with diff + cascade delete.","operationId":"change_scope_v1_enterprise_sources__connector_id__scope_patch","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeChangeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScopeChangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/sync":{"post":{"tags":["Enterprise"],"summary":"Sync Connector","description":"Trigger a full sync for the given connector.","operationId":"sync_connector_v1_enterprise_connectors__connector_id__sync_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/gdrive/webhook":{"post":{"tags":["Enterprise"],"summary":"Gdrive Webhook","description":"Receive a Google Drive ``changes.watch`` push notification.\n\nGoogle sends metadata in headers, not a body. The handler:\n\n  1. Acks the initial ``sync`` state (subscription confirmation)\n     with 200 and does nothing else.\n  2. For real notifications, the framework's\n     :data:`gdrive_webhook_verifier` validates\n     ``X-Goog-Channel-Token`` against the per-connector secret\n     via ``hmac.compare_digest`` (M1 fix). Verification failures\n     (including unknown channel under the L1 fix) silently log\n     and return 200 to avoid Google's 72-hour retry storm.\n  3. Schedule background ``delta_sync``. Returns 200 within\n     Google's ~30s timeout.","operationId":"gdrive_webhook_v1_enterprise_connectors_gdrive_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/enterprise/connectors/{connector_id}/folders":{"get":{"tags":["Enterprise"],"summary":"List Connector Folders","description":"List Google Drive folders for folder selection UI.","operationId":"list_connector_folders_v1_enterprise_connectors__connector_id__folders_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FolderListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Enterprise"],"summary":"Update Connector Folders","description":"Update which Google Drive folders to index for a connector.","operationId":"update_connector_folders_v1_enterprise_connectors__connector_id__folders_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFoldersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/setup-mode":{"post":{"tags":["Enterprise"],"summary":"Set Connector Setup Mode","description":"Persist the customer's setup-mode pick and probe for root files.\n\nStream 3A — replaces the implicit \"open the picker\" step that every\nper-connector flow had. The frontend posts the user's choice here\nand receives ``has_root_files`` so it can decide whether to render\nthe \"include root files\" checkbox in the next step.","operationId":"set_connector_setup_mode_v1_enterprise_connectors__connector_id__setup_mode_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupModeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupModeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/start-sync":{"post":{"tags":["Enterprise"],"summary":"Start Connector Sync","description":"Confirm the picker selection and schedule the initial sync.\n\nReturns 202 immediately; the actual sync runs as a durable SQS-owned\nworker job via ``sync_coordinator.run_initial_sync``. The\ndouble-confirmation guard (``confirmation_token``) lets the frontend\nretry the modal submit safely — duplicate clicks reuse the same job.","operationId":"start_connector_sync_v1_enterprise_connectors__connector_id__start_sync_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartSyncRequest"}}}},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StartSyncResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/sync-status":{"get":{"tags":["Enterprise"],"summary":"Get Connector Sync Status","description":"Return the current sync state + progress for a connector.\n\nState is derived from the lifecycle ``connector.status`` column:\n\n- ``pending``                    → ``not_started``\n- ``syncing``                    → ``syncing``\n- ``connected`` w/ last_sync_status='success' → ``completed``\n- ``connected`` w/ last_sync_status='partial' → ``completed`` (with errors)\n- ``error``                      → ``failed``","operationId":"get_connector_sync_status_v1_enterprise_connectors__connector_id__sync_status_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SyncStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/import-issues":{"get":{"tags":["Enterprise"],"summary":"List Connector Import Issues","description":"Return the tenant-scoped missing-file/import-failure log.","operationId":"list_connector_import_issues_v1_enterprise_connectors__connector_id__import_issues_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}},{"name":"sync_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Sync Id"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string","pattern":"^(skipped|failed)$"},{"type":"null"}],"title":"Status"}},{"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"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConnectorImportIssuesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/new-folders":{"get":{"tags":["Enterprise"],"summary":"List Connector New Folders","description":"Return the new-folders review list for the connector.\n\nMode-aware: ``everything`` returns ``new_folders_found``\n(informational, will be auto-included on next sync), ``selective``\nreturns ``new_folders_pending_review`` (require approve/dismiss).","operationId":"list_connector_new_folders_v1_enterprise_connectors__connector_id__new_folders_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NewFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/approve-new-folders":{"post":{"tags":["Enterprise"],"summary":"Approve Connector New Folders","description":"Add the requested folder IDs to the connector's selection.\n\nSelective mode workflow: customer reviews\n``new_folders_pending_review``, picks a subset, this endpoint\nappends them to the active selection (``folder_ids`` or ``scope``)\nand removes them from the pending list. Folder IDs not on the\npending list are silently ignored — keeps the call idempotent\nunder retry.","operationId":"approve_connector_new_folders_v1_enterprise_connectors__connector_id__approve_new_folders_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveNewFoldersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveNewFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/dismiss-new-folders":{"post":{"tags":["Enterprise"],"summary":"Dismiss Connector New Folders","description":"Remove folders from the pending-review list without including them.\n\nCustomer chose to ignore these specific folders. They stay in\n``last_seen_folders`` so future syncs do not re-detect them — the\nonly way they reappear is if they get deleted upstream and then\nre-created.","operationId":"dismiss_connector_new_folders_v1_enterprise_connectors__connector_id__dismiss_new_folders_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissNewFoldersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissNewFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/sharepoint":{"get":{"tags":["Enterprise"],"summary":"Sharepoint Auth","description":"Return the Azure AD OAuth consent URL for SharePoint integration.\n\nGenerates a CSRF token stored in Redis and carries tenant_id + csrf in\nthe OAuth state parameter so the public callback can identify the\ntenant without auth headers. Matches the GDrive pattern exactly.","operationId":"sharepoint_auth_v1_enterprise_connect_sharepoint_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/sharepoint/callback":{"get":{"tags":["Enterprise"],"summary":"Sharepoint Callback","description":"Exchange Azure AD OAuth code for tokens and create the connector row.\n\nPublic endpoint (no auth dependency) — Azure AD redirects the browser\nhere. Tenant identity is carried in the signed OAuth state parameter\nand validated against a Redis-stored CSRF token.","operationId":"sharepoint_callback_v1_enterprise_connect_sharepoint_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Azure AD","title":"Code"},"description":"OAuth authorization code from Azure AD"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Azure AD","title":"Error"},"description":"OAuth error code from Azure AD"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Azure AD","title":"Error Description"},"description":"Human-readable error description from Azure AD"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/sharepoint/sites":{"get":{"tags":["Enterprise"],"summary":"List Sharepoint Sites","description":"Return sites the authenticated admin can pick from (and drives if chosen).\n\nPage-bounded since v0.5.22 Phase 2 (CC-9). The default page batch\nis 5 Graph pages ≈ 1000 sites. Larger tenants page in via the FE\nadapter loop on ``next_cursor``. ``selected_scope`` and the legacy\nsingle-drive fields are echoed on every page so the FE can render\npersisted state without waiting for the loop to complete.","operationId":"list_sharepoint_sites_v1_enterprise_connectors__connector_id__sharepoint_sites_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}},{"name":"search","in":"query","required":false,"schema":{"type":"string","description":"Filter sites by name","default":"*","title":"Search"},"description":"Filter sites by name"},{"name":"site_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"If set, also return drives within this site","title":"Site Id"},"description":"If set, also return drives within this site"},{"name":"after","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pagination cursor (Graph ``@odata.nextLink``) returned by the previous page's ``next_cursor``. Omit for first page. v0.5.22 Phase 2 / CC-9.","title":"After"},"description":"Pagination cursor (Graph ``@odata.nextLink``) returned by the previous page's ``next_cursor``. Omit for first page. v0.5.22 Phase 2 / CC-9."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointSitesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/sharepoint/start-sync":{"post":{"tags":["Enterprise"],"summary":"Start Sharepoint Sync","description":"Persist the admin's site/drive pick, create the Graph webhook, sync.\n\nGraph subscriptions expire within 3 days — the\n``sharepoint_webhook_renewal`` nightly job (04:15 UTC) PATCHes the\nexpiration. This endpoint only creates the initial subscription and\ntriggers the first full delta crawl.","operationId":"start_sharepoint_sync_v1_enterprise_connectors__connector_id__sharepoint_start_sync_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointStartSyncRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointStartSyncResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/sharepoint/scope":{"post":{"tags":["Enterprise"],"summary":"Update Sharepoint Scope","description":"Persist the admin's multi-site sync scope for a SharePoint connector.\n\nWrites ``config[\"scope\"]`` as a list of ``{site_id, drive_ids}`` entries.\nAn empty ``drive_ids`` list means \"every drive on this site\" — expanded\nat sync time.\n\nSubscription lifecycle (v0.4.10): diffs the new scope against the\npreviously-persisted ``webhook_subscriptions`` list and creates Graph\nsubscriptions for newly added (site_id, drive_id) pairs, deletes\nsubscriptions for removed pairs, and leaves unchanged pairs alone. The\nlegacy single-field ``webhook_subscription_id`` from v0.4.2 is\npreserved — pre-v0.4.10 connectors continue to work until their next\nscope save bootstraps a subscription for that drive (at which point\nthe legacy fields are cleared to avoid a duplicate subscription for\nthe same drive).","operationId":"update_sharepoint_scope_v1_enterprise_connectors__connector_id__sharepoint_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SharepointScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/box":{"get":{"tags":["Enterprise"],"summary":"Box Auth","description":"Return the Box OAuth consent URL for this tenant's admin.\n\nState carries tenant_id + a CSRF token stored in Redis. The public\ncallback endpoint validates the CSRF token before creating the\nconnector row.","operationId":"box_auth_v1_enterprise_connect_box_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoxAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/box/callback":{"get":{"tags":["Enterprise"],"summary":"Box Callback","description":"Exchange Box OAuth code for tokens and create the connector row.\n\nPublic endpoint (no auth dependency) — Box redirects the browser\nhere. Tenant identity is carried in the signed OAuth state parameter\nand validated against a Redis-stored CSRF token.","operationId":"box_callback_v1_enterprise_connect_box_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Box","title":"Code"},"description":"OAuth authorization code from Box"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Box (e.g. invalid_scope, access_denied)","title":"Error"},"description":"OAuth error code from Box (e.g. invalid_scope, access_denied)"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Box","title":"Error Description"},"description":"Human-readable error description from Box"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/box/folders":{"get":{"tags":["Enterprise"],"summary":"List Box Folders","description":"Return sub-folders of the given Box folder for the picker UI.\n\nBox has a single folder hierarchy — no \"sites\" concept. The picker\nwalks the tree one level at a time starting from folder id ``\"0\"``\n(the root). Returned items are always folders; files are not\nreturned here (the scope is folder-level in Box).","operationId":"list_box_folders_v1_enterprise_connectors__connector_id__box_folders_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}},{"name":"parent_id","in":"query","required":false,"schema":{"type":"string","description":"Box folder id to list children of. '0' is the root.","default":"0","title":"Parent Id"},"description":"Box folder id to list children of. '0' is the root."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoxFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/box/scope":{"post":{"tags":["Enterprise"],"summary":"Update Box Scope","description":"Persist the admin's Box folder scope.\n\nWrites ``config[\"scope\"]`` as a list of\n``{\"location_id\": folder_id, \"sub_location_ids\": []}`` entries.\n\nWebhook subscriptions are NOT created at v0.5.15 W23 — the\n``manage_webhooks`` OAuth scope was dropped to support Box\nIndividual / Starter plan tiers, and ``ENABLE_BOX_WEBHOOKS`` is\nFalse. Removed-folder webhooks ARE deleted so subscriptions\npersisted by pre-W23 deployments get cleaned up when the operator\nedits scope. Sync now relies on the 30-minute connector_sync poll.","operationId":"update_box_scope_v1_enterprise_connectors__connector_id__box_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoxScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BoxScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/box/webhook":{"post":{"tags":["Enterprise"],"summary":"Box Webhook","description":"Box webhook notification handler.\n\nBox calls this endpoint when a file changes in a subscribed folder.\nAuthenticity is verified via ``BOX-Signature-Primary`` /\n``BOX-Signature-Secondary`` (HMAC-SHA256 over body + timestamp).\n\nReturns 200 for both accepted and signature-mismatch notifications\n(we never 401 — Box treats 4xx as a hard failure and retries\nindefinitely, which would flood our logs during key rotation).\nThe actual delta sync runs in a background asyncio task so the\nwebhook response lands inside Box's 10-second delivery SLO.","operationId":"box_webhook_v1_enterprise_connect_box_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Box Webhook V1 Enterprise Connect Box Webhook Post"}}}}}}},"/v1/enterprise/connect/dropbox":{"get":{"tags":["Enterprise"],"summary":"Dropbox Auth","description":"Return the Dropbox OAuth consent URL for this tenant's admin.\n\nState carries tenant_id + a CSRF token stored in Redis. The\npublic callback endpoint validates the CSRF token before\ncreating the connector row.","operationId":"dropbox_auth_v1_enterprise_connect_dropbox_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropboxAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/dropbox/callback":{"get":{"tags":["Enterprise"],"summary":"Dropbox Callback","description":"Exchange Dropbox OAuth code for tokens and create the connector row.\n\nPublic endpoint (no auth dependency) — Dropbox redirects the\nbrowser here. Tenant identity is carried in the signed OAuth\nstate parameter and validated against a Redis-stored CSRF token.","operationId":"dropbox_callback_v1_enterprise_connect_dropbox_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Dropbox","title":"Code"},"description":"OAuth authorization code from Dropbox"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Dropbox","title":"Error"},"description":"OAuth error code from Dropbox"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Dropbox","title":"Error Description"},"description":"Human-readable error description from Dropbox"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/dropbox/folders":{"get":{"tags":["Enterprise"],"summary":"List Dropbox Folders","description":"Return sub-folders of the given Dropbox folder for the picker UI.\n\nWalks the user's Dropbox tree one level at a time via a\nnon-recursive ``list_folder``. Only folder entries are returned;\nfiles are not surfaced because the scope is folder-level.","operationId":"list_dropbox_folders_v1_enterprise_connectors__connector_id__dropbox_folders_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}},{"name":"parent_path","in":"query","required":false,"schema":{"type":"string","description":"Dropbox folder path to list children of. '' (empty) is the root.","default":"","title":"Parent Path"},"description":"Dropbox folder path to list children of. '' (empty) is the root."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropboxFoldersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/dropbox/scope":{"post":{"tags":["Enterprise"],"summary":"Update Dropbox Scope","description":"Persist the admin's Dropbox folder scope.\n\nDropbox's webhook is app-level (one URL for the entire app), so\nunlike Box and SharePoint this endpoint does NOT create or delete\nper-folder subscriptions. It only persists the list of paths to\nsync. The webhook handler triggers delta sync for all connectors\nwhose ``account_id`` appears in the incoming payload — regardless\nof which folder changed.\n\nPer-folder delta cursors are preserved when a folder stays in\nscope; dropped when a folder is removed; fresh (empty) for\nnewly-added folders so the next sync does a full initial crawl.","operationId":"update_dropbox_scope_v1_enterprise_connectors__connector_id__dropbox_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropboxScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DropboxScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/dropbox/webhook":{"get":{"tags":["Enterprise"],"summary":"Dropbox Webhook Challenge","description":"Dropbox webhook subscription challenge handler.\n\nWhen the admin registers our webhook URL in the Dropbox App\nConsole, Dropbox sends a GET with ``?challenge=...`` expecting\nthe exact token echoed back as ``text/plain`` with\n``X-Content-Type-Options: nosniff``. No auth headers are sent.","operationId":"dropbox_webhook_challenge_v1_enterprise_connect_dropbox_webhook_get","parameters":[{"name":"challenge","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Challenge"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Dropbox Webhook Challenge V1 Enterprise Connect Dropbox Webhook Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Enterprise"],"summary":"Dropbox Webhook","description":"Dropbox app-level webhook notification handler.\n\nCalled when ANY user's Dropbox content changes. The payload:\n    {\"list_folder\": {\"accounts\": [\"dbid:xxx\", ...]},\n     \"delta\": {\"users\": [12345, ...]}}\nlists the account_ids that have pending changes. We match those\nto connectors via ``DropboxConnector`` configs and run delta\nsync for each.\n\nAuthenticity verified via ``X-Dropbox-Signature`` (HMAC-SHA256\nhex digest of the raw body with the app secret). Signature\nfailures return 200 with ``{\"accepted\": 0, \"rejected\": 1}`` —\nDropbox treats 4xx responses as permanent failures and will\ndisable the webhook after enough of them, which would be\ncatastrophic during a key rotation.","operationId":"dropbox_webhook_v1_enterprise_connect_dropbox_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Dropbox Webhook V1 Enterprise Connect Dropbox Webhook Post"}}}}}}},"/v1/enterprise/connect/confluence":{"get":{"tags":["Enterprise"],"summary":"Confluence Auth","description":"Return the Atlassian OAuth consent URL for this tenant's admin.\n\nState carries tenant_id + a CSRF token stored in Redis. The\npublic callback endpoint validates the CSRF token before\ncreating the connector row.","operationId":"confluence_auth_v1_enterprise_connect_confluence_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfluenceAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/confluence/callback":{"get":{"tags":["Enterprise"],"summary":"Confluence Callback","description":"Exchange Atlassian OAuth code for tokens + cloud_id and create connector row.\n\nPublic endpoint (no auth dependency) — Atlassian redirects the\nbrowser here. The callback handler also performs the\naccessible-resources lookup to resolve the Confluence site's\ncloud_id, which is required for every subsequent API call.","operationId":"confluence_callback_v1_enterprise_connect_confluence_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Atlassian","title":"Code"},"description":"OAuth authorization code from Atlassian"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Atlassian","title":"Error"},"description":"OAuth error code from Atlassian"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Atlassian","title":"Error Description"},"description":"Human-readable error description from Atlassian"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/confluence/spaces":{"get":{"tags":["Enterprise"],"summary":"List Confluence Spaces Endpoint","description":"Return spaces the authenticated admin can pick from.","operationId":"list_confluence_spaces_endpoint_v1_enterprise_connectors__connector_id__confluence_spaces_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfluenceSpacesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/confluence/scope":{"post":{"tags":["Enterprise"],"summary":"Update Confluence Scope","description":"Persist the admin's Confluence space scope and create/drop webhooks.\n\nWrites ``config[\"scope\"]`` as a list of\n``{\"location_id\": space_key, \"sub_location_ids\": []}`` entries\nand diffs the new space set against previously persisted\nsubscriptions. New space keys get a Confluence webhook;\nremoved keys have their subscription deleted.\n\nConfluence webhooks do not expire — no ``expires_at`` tracking\nbeyond what Atlassian surfaces on its own.","operationId":"update_confluence_scope_v1_enterprise_connectors__connector_id__confluence_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfluenceScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfluenceScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/confluence/webhook/{token}":{"post":{"tags":["Enterprise"],"summary":"Confluence Webhook Secure","description":"Secure Confluence webhook receiver — C1 fix.\n\nThe ``{token}`` path segment carries the per-subscription URL-bound\nsecret generated at subscription create time. The framework's\n:data:`confluence_webhook_verifier` validates the token via\nconstant-time membership against every stored Confluence\nsubscription secret. On success, the verifier stashes the matched\nconnector ID on ``request.state.confluence_matched_connector_ids``.\n\nSee ``confluence_webhook_verifier.py`` for the full lookup logic +\nwarn-only rollout sequencing.","operationId":"confluence_webhook_secure_v1_enterprise_connect_confluence_webhook__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Confluence Webhook Secure V1 Enterprise Connect Confluence Webhook  Token  Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/confluence/webhook":{"post":{"tags":["Enterprise"],"summary":"Confluence Webhook Legacy","description":"Legacy unparameterised Confluence webhook receiver — warn-only.\n\nPre-C1 this URL was registered with every Confluence subscription\n(no path-bound secret). Post-C1 NEW subscriptions point at\n``/connect/confluence/webhook/{token}`` (see :func:`confluence_webhook_secure`)\nand carry per-subscription URL secrets. EXISTING subscriptions\ncontinue to deliver here until the v0.5.25 backfill regenerates\nthem — those customers fall back to the periodic poll\n(``confluence_periodic_sync``, CC-4) for sync continuity.\n\nThis handler intentionally does NOT dispatch — it logs the legacy\ndelivery for backfill-progress tracking and returns 200. Atlassian\ntreats 4xx as permanent failure and would disable the subscription,\nwhich is the wrong cure (the periodic poll handles the gap).\n\nAlso bundles the fix for a pre-existing routing bug from commit\n48dcf2ab (2026-04-30): the @router.post decorator on this URL was\nmisplaced onto _extract_confluence_event_space_key (the helper),\nso the legacy receiver had been dead code. After this PR the\ndecorator is correctly attached but the handler is the safe stub.\nProduction behaviour change: legacy deliveries previously returned\na spaceKey string with a 200; now they return ``{\"accepted\": 0}``\nwith a 200. No dispatch was happening before; no dispatch happens\nnow either. Customers' sync continuity is unaffected.","operationId":"confluence_webhook_legacy_v1_enterprise_connect_confluence_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Confluence Webhook Legacy V1 Enterprise Connect Confluence Webhook Post"}}}}}}},"/v1/enterprise/connect/notion":{"get":{"tags":["Enterprise"],"summary":"Notion Auth","description":"Return the Notion OAuth consent URL for this tenant's admin.\n\nState carries tenant_id + a CSRF token stored in Redis. The\npublic callback endpoint validates the CSRF token before\ncreating the connector row.","operationId":"notion_auth_v1_enterprise_connect_notion_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotionAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/notion/callback":{"get":{"tags":["Enterprise"],"summary":"Notion Callback","description":"Exchange Notion OAuth code for the permanent access token.\n\nPublic endpoint (no auth dependency) — Notion redirects the\nbrowser here. The callback also captures ``workspace_id`` and\n``workspace_name`` for the Library UI to surface.","operationId":"notion_callback_v1_enterprise_connect_notion_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Notion","title":"Code"},"description":"OAuth authorization code from Notion"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Notion","title":"Error"},"description":"OAuth error code from Notion"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Notion","title":"Error Description"},"description":"Human-readable error description from Notion"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/notion/items":{"get":{"tags":["Enterprise"],"summary":"List Notion Items","description":"Return every page + database the integration has access to.\n\nNotion requires the user to manually share each page/database\nwith the bot via the \"Add connections\" menu — items not\nshared are invisible even with a valid access token.","operationId":"list_notion_items_v1_enterprise_connectors__connector_id__notion_items_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotionPickerResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/notion/scope":{"post":{"tags":["Enterprise"],"summary":"Update Notion Scope","description":"Persist the admin's Notion page/database scope.\n\nNotion has no webhooks, so this endpoint does NOT create any\nsubscriptions. It only writes ``config[\"scope\"]`` and resets\nthe workspace-level delta cursor so the next poll does a\nfull walk of the newly-added items.","operationId":"update_notion_scope_v1_enterprise_connectors__connector_id__notion_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotionScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotionScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/slack":{"get":{"tags":["Enterprise"],"summary":"Slack Auth","description":"Return the Slack OAuth V2 consent URL for this tenant's admin.\n\nState carries tenant_id + a CSRF token stored in Redis. The\npublic callback validates the CSRF token before persisting\nthe bot token.","operationId":"slack_auth_v1_enterprise_connect_slack_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlackAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/slack/callback":{"get":{"tags":["Enterprise"],"summary":"Slack Callback","description":"Exchange the OAuth code for a permanent bot token.\n\nPublic endpoint (no auth dependency) — Slack redirects the\nbrowser here. The callback captures ``team_id`` so the\nwebhook handler can route future events to this tenant.","operationId":"slack_callback_v1_enterprise_connect_slack_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Slack","title":"Code"},"description":"OAuth authorization code from Slack"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Slack","title":"Error"},"description":"OAuth error code from Slack"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Slack","title":"Error Description"},"description":"Human-readable error description from Slack"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/slack/channels":{"get":{"tags":["Enterprise"],"summary":"List Slack Channels Route","description":"Return every channel the bot has access to for the picker UI.","operationId":"list_slack_channels_route_v1_enterprise_connectors__connector_id__slack_channels_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlackChannelsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/slack/scope":{"post":{"tags":["Enterprise"],"summary":"Update Slack Scope","description":"Persist the admin's Slack channel scope + score threshold.\n\nEvents API subscriptions do not need to be created here —\nthe bot's workspace-level installation (from OAuth) already\nsubscribes to every event registered in the Slack App Console.\nWe only need to tell the connector which channels to index.","operationId":"update_slack_scope_v1_enterprise_connectors__connector_id__slack_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlackScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SlackScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/gmail":{"get":{"tags":["Enterprise"],"summary":"Gmail Auth","description":"Return the Google OAuth consent URL for Gmail readonly scope.\n\nPer-user model (spec §4): any tenant member can connect their own\nGmail; no admin gate. The consent screen shown by Google requests\nonly ``gmail.readonly`` even though the underlying OAuth client is\nshared with the Drive connector (one Google Cloud project, two\nflows — see spec §3 and ``gmail_connector.py`` module docstring).\n\nReturns 503 if the global kill switch ``enable_gmail_connector`` is\noff in Settings — operator-controlled rollout knob.","operationId":"gmail_auth_v1_enterprise_connect_gmail_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GmailAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/gmail/callback":{"get":{"tags":["Enterprise"],"summary":"Gmail Callback","description":"Gmail OAuth callback — exchanges code for tokens, upserts connector row.\n\nPublic endpoint (no auth dependency) — Google redirects the browser\nhere. State carries ``tenant_id``, ``member_id``, ``csrf``; the CSRF\ntoken is validated against its Redis entry before any token exchange.\n\nOn success: creates (or updates) an EnterpriseConnector row with\n``connector_type='gmail'``, ``status='connected'``, ``created_by_id``\n= the initiating member. Re-install of the same mailbox UPDATEs in\nplace (per partial unique index uq_active_gmail_connector_per_tenant_user)\nand preserves ``last_history_id`` so PR 4's delta_sync doesn't\ntrigger a full re-scan.\n\nError handling per 2026-05-25 Slack OAuth audit Q4: token-exchange\nfailures redirect via :func:`_oauth_error_redirect` (banner UI),\nnever raise 502. Provider ``?error=`` is similarly redirected.\nMissing code / invalid state / bad CSRF raise 400/403 (rare —\nindicates a malformed callback URL or expired CSRF token).","operationId":"gmail_callback_v1_enterprise_connect_gmail_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id, member_id, and CSRF token","title":"State"},"description":"OAuth state with tenant_id, member_id, and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Google","title":"Code"},"description":"OAuth authorization code from Google"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Google (RFC 6749 §4.1.2.1)","title":"Error"},"description":"OAuth error code from Google (RFC 6749 §4.1.2.1)"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Google","title":"Error Description"},"description":"Human-readable error description from Google"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/gmail/disconnect":{"post":{"tags":["Enterprise"],"summary":"Gmail Disconnect","description":"Tear down a Gmail connector (self OR admin).\n\nPer spec §10: members can disconnect their own Gmail; admins can\ndisconnect anyone's. Enforced by comparing\n``connector.created_by_id == employee.member_id OR role_satisfies(role, 'admin')``.\n\nThe connector row is marked ``status='disconnected'`` (NOT\nhard-deleted). Per spec §9 deletion semantics, indexed chunks\nremain in Qdrant for the standard 90-day TTL — search results from\nthe indexed mailbox continue surfacing during the window. If the\nuser wants immediate deletion they go through a workspace admin\n(TODO: dedicated admin-driven hard-delete endpoint in v0.6).\n\nToken revocation with Google is best-effort: a 5xx from Google\ndoes NOT block the row teardown. The user can revoke the app\nmanually from their Google Account permissions page if our call\nfails.","operationId":"gmail_disconnect_v1_enterprise_connectors__connector_id__gmail_disconnect_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GmailDisconnectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/slack/webhook":{"post":{"tags":["Enterprise"],"summary":"Slack Webhook","description":"Slack Events API handler.\n\nTwo shapes:\n  (1) URL verification challenge — Slack POSTs a body with\n      ``type: \"url_verification\"`` and ``challenge: \"xxx\"``.\n      We MUST respond with ``{\"challenge\": \"xxx\"}`` to prove\n      we own the endpoint.\n  (2) Event callback — Slack POSTs a body with\n      ``type: \"event_callback\"`` and an ``event`` payload.\n      We verify the HMAC-SHA256 signature + replay timestamp\n      before dispatching.\n\nSecurity:\n  * ``X-Slack-Signature`` is verified against every connector's\n    stored signing_secret (multiple installs can share the\n    signing secret because the Slack App Console is one app).\n  * Stale requests (timestamp older than 5 min) are rejected.\n  * Unknown ``team_id`` logs ``slack_webhook_unknown_subscription``\n    and returns 202 (contract §6.4) — never 401.\n\nBackground delta sync is triggered per matched connector.","operationId":"slack_webhook_v1_enterprise_connect_slack_webhook_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Slack Webhook V1 Enterprise Connect Slack Webhook Post"}}}}}}},"/v1/enterprise/connect/teams":{"get":{"tags":["Enterprise"],"summary":"Teams Auth","description":"Return the Azure AD OAuth consent URL for Microsoft Teams.\n\nState carries tenant_id + a CSRF token stored in Redis. The\npublic callback validates the CSRF token before persisting the\nreturned access / refresh tokens.","operationId":"teams_auth_v1_enterprise_connect_teams_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamsAuthUrlResponse"}}}}}}},"/v1/enterprise/connect/teams/callback":{"get":{"tags":["Enterprise"],"summary":"Teams Callback","description":"Exchange the OAuth authorization code for tokens and persist the connector.","operationId":"teams_callback_v1_enterprise_connect_teams_callback_get","parameters":[{"name":"state","in":"query","required":true,"schema":{"type":"string","description":"OAuth state with tenant_id and CSRF token","title":"State"},"description":"OAuth state with tenant_id and CSRF token"},{"name":"code","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth authorization code from Azure AD","title":"Code"},"description":"OAuth authorization code from Azure AD"},{"name":"error","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"OAuth error code from Azure AD","title":"Error"},"description":"OAuth error code from Azure AD"},{"name":"error_description","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Human-readable error description from Azure AD","title":"Error Description"},"description":"Human-readable error description from Azure AD"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/teams/channels":{"get":{"tags":["Enterprise"],"summary":"List Teams Channels Route","description":"Return every team + channel the authenticated user can access.","operationId":"list_teams_channels_route_v1_enterprise_connectors__connector_id__teams_channels_get","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamsChannelsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/teams/scope":{"post":{"tags":["Enterprise"],"summary":"Update Teams Scope","description":"Persist the admin's Teams channel scope + score threshold.\n\n``channel_keys`` are composite ``{team_id}:{channel_id}`` strings.\nThe connector stores them in ``config[\"scope\"]`` as\n``{\"location_id\": key, \"sub_location_ids\": []}`` entries so the\nshape matches every other multi-location connector (contract §2).","operationId":"update_teams_scope_v1_enterprise_connectors__connector_id__teams_scope_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamsScopeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TeamsScopeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connect/sharepoint/webhook":{"post":{"tags":["Enterprise"],"summary":"Sharepoint Webhook","description":"Graph subscription notification handler.\n\nTwo shapes:\n  (1) Validation probe — Graph sends a POST with `?validationToken=...`\n      on subscription creation. We MUST echo the token as plain text\n      within 10 seconds or the subscription is rejected. No auth,\n      no body parse — return the token immediately.\n  (2) Notification — Graph POSTs a JSON body when items change. The\n      framework's :data:`sharepoint_webhook_verifier` checks each\n      notification entry's ``clientState`` against the stored\n      subscription secret via ``hmac.compare_digest`` (M2 fix).\n      ``per_entry_mode=\"all_or_nothing\"`` rejects the whole batch\n      if any entry has an invalid clientState — preserves the\n      pre-fix posture and defends against forged-entry padding.\n\nThis route is PUBLIC — Graph does not send auth headers. All security\ncomes from clientState matching + short-lived subscription lifetimes.","operationId":"sharepoint_webhook_v1_enterprise_connect_sharepoint_webhook_post","parameters":[{"name":"validationToken","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Validationtoken"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"title":"Response Sharepoint Webhook V1 Enterprise Connect Sharepoint Webhook Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/test":{"post":{"tags":["Enterprise"],"summary":"S3 Test","description":"Validate an S3 connector config WITHOUT creating a connector record.\n\nv0.5.15 W15 HIGH #10. Lets the operator iterate on the IAM trust\npolicy + permissions policy until the test passes, then click\nConnect to commit the working config. Without this, every\ninvalid-IAM iteration leaves an ``status=error`` connector row\nbehind that the operator must disconnect via the kebab menu\nbefore retrying.\n\nAlways returns 200 with ``success: true|false``. Frontend reads\n``error_kind`` for variant-specific UX (e.g. show the trust\npolicy snippet for ``role_assume_failed``).","operationId":"s3_test_v1_enterprise_connectors_s3_test_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3TestRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3TestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/connect":{"post":{"tags":["Enterprise"],"summary":"S3 Connect","description":"Connect a customer's S3 bucket via cross-account IAM role assumption.\n\nThe legacy \"managed\" mode (Quelvio creates a per-tenant bucket for the\ncustomer to upload to) was removed in v0.5.15 W15 CRIT #6 per CEO Q1\nruling. Pilots without their own S3 bucket use the Upload connector\ninstead. Pydantic enforces ``mode == \"cross_account\"`` via the schema's\nLiteral — a request with any other ``mode`` value is rejected with\n422 before this handler runs.\n\nB+ Phase 3E — ``permission_emails`` lands on the connector config\nJSONB. Every chunk synced from this connector inherits the config\nvalue; per-prefix rules are deferred to a future S3 v2.","operationId":"s3_connect_v1_enterprise_connectors_s3_connect_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3ConnectRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3ConnectResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/setup-init":{"post":{"tags":["Enterprise"],"summary":"S3 Setup Init","description":"v0.6 Pattern A wizard Step 1 — create pending connector + return trust template.\n\nGenerates a fresh ExternalId via secrets.token_urlsafe(32) and shows\nit to the customer ONCE. The customer copies it into the IAM trust\npolicy on their side. If they lose the value before completing\nStep 3, they delete the connector and re-run setup-init.\n\nB+ Phase 3E — ``permission_emails`` lands on the connector config\nJSONB so the resolver returns the operator-defined ACL at every\nfile sync. Per design §3.8 + §3.9: S3 has no email-level ACL\nprimitive — operator-defined is the only honest design.","operationId":"s3_setup_init_v1_enterprise_connectors_s3_setup_init_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3SetupInitRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3SetupInitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/{connector_id}/s3/rotate-external-id":{"post":{"tags":["Enterprise"],"summary":"S3 Rotate External Id","description":"v0.7.6 Reconfigure entry — rotate ExternalId on an existing S3 connector.\n\nAllowed states: ``connected`` (the canonical case — operator wants\nto refresh the trust-policy binding) or ``error`` (legacy v0.7.5\nrows still in the deploy window — Reconfigure is the recovery path\nfor those too). Other states raise 400.","operationId":"s3_rotate_external_id_v1_enterprise_connectors__connector_id__s3_rotate_external_id_post","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3RotateExternalIdResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/verify":{"post":{"tags":["Enterprise"],"summary":"S3 Verify","description":"v0.6 Pattern A wizard Step 3 — start async verify job.\n\nReturns 202 Accepted immediately. The wizard polls\nGET /connectors/s3/verify/{job_id} until status is success or\nfailed. Background preflight runs at offsets [0, 5, 20, 65]s —\nretries on the two transient codes\n(quelvio_iam_misconfigured + role_not_in_quelvio_allowlist),\nfast-fails on the six customer-side codes.","operationId":"s3_verify_v1_enterprise_connectors_s3_verify_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3VerifyRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3VerifyJobAcceptedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/verify/{job_id}":{"get":{"tags":["Enterprise"],"summary":"S3 Verify Status","description":"Poll the status of an async verify job.\n\nWizard polls every 2s. Synthetic timeout: if elapsed > VERIFY_BUDGET_MS\nand status is still pending, we surface a failed response so the\ncustomer isn't left staring at a spinner. The DB row stays pending\n(a worker process restart could orphan it) — the nightly cleanup\nsweep removes it via expires_at.","operationId":"s3_verify_status_v1_enterprise_connectors_s3_verify__job_id__get","parameters":[{"name":"job_id","in":"path","required":true,"schema":{"type":"string","title":"Job Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3VerifyStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/objects":{"post":{"tags":["Enterprise"],"summary":"S3 Objects","description":"List objects under a prefix in the connector's bucket.\n\nAlways 200 — failure modes (assume_role denied, bucket access\ndenied, etc.) carry a structured ``failure`` field rather than\nsurfacing as 4xx, so the wizard renders the customer_action text\ninline rather than treating it as a network error.","operationId":"s3_objects_v1_enterprise_connectors_s3_objects_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3ObjectsRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3ObjectsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/setup/{connector_id}":{"delete":{"tags":["Enterprise"],"summary":"S3 Setup Cancel","description":"Cancel a pending S3 setup. Hard-delete + allowlist cleanup.\n\nAllowed states: ``pending_role_setup``, ``error``.\nStatus='connected' returns 409 with body\n``{\"error\": \"use_disconnect_endpoint\"}`` — the customer must use\nthe generic ``/sources/{id}`` disconnect endpoint for active\nconnectors.\n\nIdempotent: a second DELETE on the same id returns 404 (the row\nis gone). Frontend should treat 404 as \"already cleaned up\" rather\nthan an error.","operationId":"s3_setup_cancel_v1_enterprise_connectors_s3_setup__connector_id__delete","parameters":[{"name":"connector_id","in":"path","required":true,"schema":{"type":"string","title":"Connector Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/connectors/s3/detect-region":{"post":{"tags":["Enterprise"],"summary":"S3 Detect Region","description":"HEAD the bucket's public endpoint and read x-amz-bucket-region.\n\nReturns 400 only on invalid bucket-name input; all other failures\nsurface as ``{\"region\": null, \"reason\": ...}``.","operationId":"s3_detect_region_v1_enterprise_connectors_s3_detect_region_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3DetectRegionRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/S3DetectRegionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/billing/balance":{"get":{"tags":["Enterprise"],"summary":"Billing Balance","description":"Return current Knowledge Token balance for the tenant.","operationId":"billing_balance_v1_enterprise_billing_balance_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenBalanceResponse"}}}}}}},"/v1/enterprise/billing/consumption":{"get":{"tags":["Enterprise"],"summary":"Billing Consumption","description":"Return token consumption breakdown by dimension for a billing period.","operationId":"billing_consumption_v1_enterprise_billing_consumption_get","parameters":[{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"'current' for this calendar month","default":"current","title":"Period"},"description":"'current' for this calendar month"},{"name":"start","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Period start (ISO 8601)","title":"Start"},"description":"Period start (ISO 8601)"},{"name":"end","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Period end (ISO 8601)","title":"End"},"description":"Period end (ISO 8601)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConsumptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/billing/projection":{"get":{"tags":["Enterprise"],"summary":"Billing Projection","description":"Project monthly token cost based on current-period run rate.","operationId":"billing_projection_v1_enterprise_billing_projection_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProjectionResponse"}}}}}}},"/v1/enterprise/billing/setup-payment":{"post":{"tags":["Enterprise"],"summary":"Billing Setup Payment","description":"Provision a Stripe Customer for the tenant (lazy) and return a Customer\nPortal session URL. The frontend redirects the user there to attach a\npayment method. After completing or canceling, Stripe redirects the user\nback to ``https://enterprise.quelvio.com/billing``.\n\nIdempotent — calling twice for the same tenant returns a fresh portal\nsession against the existing Stripe Customer (no duplicate Customer is\ncreated).","operationId":"billing_setup_payment_v1_enterprise_billing_setup_payment_post","requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/SetupPaymentRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetupPaymentResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/billing/payment-status":{"get":{"tags":["Enterprise"],"summary":"Billing Payment Status","description":"Return whether the tenant has a card on file (and its display details).\n\nProvisions the Stripe Customer lazily on first call so the existing test\ntenant is backfilled automatically when the billing page first loads.","operationId":"billing_payment_status_v1_enterprise_billing_payment_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentStatusResponse"}}}}}}},"/v1/enterprise/billing/report-usage":{"post":{"tags":["Enterprise"],"summary":"Billing Report Usage","description":"Manual stopgap for the nightly Stripe batch Lambda.\n\nReads unprocessed ``token_consumption`` rows and posts each as a Stripe\nBilling MeterEvent. Idempotent — Stripe deduplicates per ``identifier``,\nand we mark each row ``processed_at = now()`` after a successful POST so\nre-runs skip them.\n\nSandbox short-circuit: returns ``skipped_sandbox=True`` with zero events\nwhen ``STRIPE_MODE != \"live\"`` (the live meter IDs do not exist in\nsandbox — see CLAUDE.md option c).","operationId":"billing_report_usage_v1_enterprise_billing_report_usage_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional — when set, only this tenant's rows are reported.","title":"Tenant Id"},"description":"Optional — when set, only this tenant's rows are reported."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReportUsageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content/upload":{"post":{"tags":["Enterprise"],"summary":"Upload Content","description":"Upload a file directly for indexing.\n\nAccepts a single file via multipart/form-data. Creates EnterpriseContentPiece\n+ bridge ContentPiece, uploads to S3, and publishes to the enrichment queue.\nFollows the same pattern as the Google Drive connector sync.","operationId":"upload_content_v1_enterprise_content_upload_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_content_v1_enterprise_content_upload_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Upload Content V1 Enterprise Content Upload Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library":{"get":{"tags":["Enterprise"],"summary":"List Library","description":"List enterprise content pieces for the tenant's library view.\n\nDefault behaviour hides:\n  * pieces with ``user_deleted_at IS NOT NULL`` (v0.4.17 soft-delete)\n  * pieces with ``enrichment_status == \"user_deleted\"`` (v0.4.17)\n  * pieces with ``removed_at IS NOT NULL`` (legacy soft-hide)\n\nPass ``include_deleted=true`` to bypass the v0.4.17 user-deleted\nfilters. The legacy ``removed_at`` filter is always applied.","operationId":"list_library_v1_enterprise_library_get","parameters":[{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","description":"Admin/debug only. Include user-soft-deleted pieces in the response (v0.4.17). Default false: user_deleted pieces are hidden from the library UI.","default":false,"title":"Include Deleted"},"description":"Admin/debug only. Include user-soft-deleted pieces in the response (v0.4.17). Default false: user_deleted pieces are hidden from the library UI."},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Cursor batch size. Use next_cursor for subsequent batches.","default":50,"title":"Page Size"},"description":"Cursor batch size. Use next_cursor for subsequent batches."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Q"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Source"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Format"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":4096},{"type":"null"}],"description":"Opaque keyset pagination cursor returned by a previous response. When provided, page offset is ignored.","title":"Cursor"},"description":"Opaque keyset pagination cursor returned by a previous response. When provided, page offset is ignored."},{"name":"sort","in":"query","required":false,"schema":{"type":"string","pattern":"^(name|source|format|author|department|authority|chunks|status|created_at|last_synced_at)$","default":"name","title":"Sort"}},{"name":"order","in":"query","required":false,"schema":{"type":"string","pattern":"^(asc|desc)$","default":"asc","title":"Order"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Library V1 Enterprise Library Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/library/summary":{"get":{"tags":["Enterprise"],"summary":"Get Library Summary","description":"Return cached library facets and aggregate summary for the tenant.","operationId":"get_library_summary_v1_enterprise_library_summary_get","parameters":[{"name":"include_deleted","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Deleted"}},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Q"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Source"}},{"name":"format","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Format"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get Library Summary V1 Enterprise Library Summary Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/tenants":{"post":{"tags":["Enterprise"],"summary":"Create Tenant","description":"Provision a new enterprise tenant (admin only).\n\nCreates the tenant record, a Qdrant collection, and the first API key.","operationId":"create_tenant_v1_enterprise_admin_tenants_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTenantRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTenantResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}},"security":[{"APIKeyHeader":[]}]}},"/v1/enterprise/admin/tenants/{tenant_id}/recreate-collection":{"post":{"tags":["Enterprise"],"summary":"Recreate Tenant Collection","description":"Delete and recreate a tenant's Qdrant collection with the correct schema.\n\nOne-time fix for tenants provisioned before named vectors were added.","operationId":"recreate_tenant_collection_v1_enterprise_admin_tenants__tenant_id__recreate_collection_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Recreate Tenant Collection V1 Enterprise Admin Tenants  Tenant Id  Recreate Collection Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/workspace":{"get":{"tags":["Enterprise"],"summary":"Get Workspace Route","description":"Return the caller's workspace summary (admin only).","operationId":"get_workspace_route_v1_enterprise_workspace_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspaceResponse"}}}}}},"delete":{"tags":["Enterprise"],"summary":"Delete Workspace Route","description":"Self-serve workspace deletion (Owner only).\n\nAtomic flow per ``DeleteOwnWorkspace.execute``:\n\n  1. Revoke every connector's OAuth tokens (best-effort, per-vendor).\n  2. Cancel Stripe subscription immediately (best-effort; cron\n     catches drift).\n  3. Walk the lifecycle state machine to ``deleted``.\n  4. Drop the Qdrant collection + content tables via\n     ``force_delete_tenant(bypass_blockers=True)``.\n\nReturns 204 on success or on idempotent re-call. Returns 409 when\nthe tenant has no active plan, when the state machine refuses the\nchain (typically because another session is mid-deletion), or\nwhen the content purge fails after the lifecycle moved.\n\nOwner-only enforcement is handled by ``require_access(OWNER, DENY)``\nabove — admin / member / suspended callers return 403 before the\nhandler runs.","operationId":"delete_workspace_route_v1_enterprise_workspace_delete","responses":{"204":{"description":"Successful Response"}}},"patch":{"tags":["Enterprise"],"summary":"Patch Workspace Route","description":"Partially update workspace name and/or member-permission flags.\n\nFields omitted from the request body are left untouched. A call\nwith no observable change is treated as idempotent — the endpoint\nreturns the current state without writing an audit event.","operationId":"patch_workspace_route_v1_enterprise_workspace_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WorkspacePatchRequest"}}},"required":true},"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"}}}}}}},"/v1/enterprise/audit":{"get":{"tags":["Enterprise"],"summary":"List Audit Events","description":"Paginated audit log for the caller's tenant. Admin role required.\n\nFilters are AND-combined. Results are ordered newest first. The total\ncount in the response reflects the filtered set, not the whole table.","operationId":"list_audit_events_v1_enterprise_audit_get","parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 lower bound","title":"Start Date"},"description":"ISO 8601 lower bound"},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 upper bound","title":"End Date"},"description":"ISO 8601 upper bound"},{"name":"actor_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by actor_id","title":"Actor Email"},"description":"Filter by actor_id"},{"name":"action","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by action","title":"Action"},"description":"Filter by action"},{"name":"event_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event_type","title":"Event Type"},"description":"Filter by event_type"},{"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"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Audit Events V1 Enterprise Audit Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/permissions":{"patch":{"tags":["Enterprise Permissions (B+ Phase 3C)"],"summary":"Bulk PATCH permission_emails across a filtered piece set (B+ Phase 3C)","description":"Bulk PATCH ``permission_emails`` on a filtered set of pieces.\n\nBehavior:\n1. Reject blank filter (422 ``filter_required``).\n2. Validate + normalise operator emails (sentinel injection 422).\n3. **Sec-H3** — if the request introduces ``__public_tenant__`` /\n   ``__public__`` via ``op='add'``, require a valid step-up token\n   via the ``X-Step-Up-Token`` header. Missing/invalid → 403.\n4. Resolve the filter to a piece-ID set, scoped to\n   ``employee.tenant_id``. Reject if count > cap (413).\n5. When ``body.dry_run=True``: short-circuit BEFORE any writes —\n   return :class:`BulkPermissionsPatchDryRunResponse` with the\n   affected count + sample IDs. The 413 cap gate still applies.\n6. **Sec-H3** — check the sliding-window rate limit (per-tenant +\n   per-actor) against the resolved affected_count. Exceeded → 429.\n7. **Manage-access rule** — filter out pieces in connector-managed\n   ``permission_resolution_mode`` (``per_file_acl`` /\n   ``workspace_inherited`` / ``connector_default`` /\n   ``explicit_public`` / ``unresolved_acl_skipped``). They are\n   reported in the response ``blocked`` list with reason\n   ``permissions_managed_by_source``; the remaining editable\n   subset is still mutated (partial-success semantics).\n8. Apply the ``op`` to each editable piece's existing\n   ``permission_emails``, persist via\n   ``UPDATE ... WHERE id IN (...)``-equivalent loop, and write\n   one enriched ``permission_audit_log`` row per piece\n   (actor_kind / actor_ip / actor_user_agent / request_id).\n9. Persist a ``permission_backfill_progress`` row keyed by\n   ``audit_log_run_id``.\n10. Schedule the Qdrant fan-out as a FastAPI background task.\n11. Return 200 with ``updated_count`` + ``audit_log_run_id`` +\n    ``blocked`` immediately.","operationId":"patch_sources_permissions_bulk_v1_enterprise_sources_permissions_patch","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkPermissionsPatchRequest"}}},"required":true},"responses":{"200":{"description":"Bulk PATCH applied. When ``dry_run=True`` was set on the request body, the response shape is ``BulkPermissionsPatchDryRunResponse`` (no mutations occurred).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkPermissionsPatchResponse","anyOf":[{"$ref":"#/components/schemas/BulkPermissionsPatchResponse"},{"$ref":"#/components/schemas/BulkPermissionsPatchDryRunResponse"}],"title":"Response Patch Sources Permissions Bulk V1 Enterprise Sources Permissions Patch"}}}},"413":{"description":"``ids``-mode only: explicit-IDs list resolves to more pieces than the per-call cap. Filter-only-mode auto-paginates instead (B-FE-14).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkPermissionsPatchCapExceeded"}}}},"429":{"description":"Sliding-window rate limit exceeded (per-tenant or per-actor 15-minute window). Sec-H3.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkPermissionsPatchRateLimited"}}}},"422":{"description":"Either the filter is blank, or the emails list failed sentinel-injection validation. Body is the shared ``PermissionErrorResponse`` shape (``error_code`` is canonical, ``error`` is the deprecated alias).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionErrorResponse"}}}},"403":{"description":"Either the caller lacks RequiredRole.ADMIN / write scope (body is the shared ``PermissionErrorResponse`` shape), OR the request would expand visibility to a public sentinel without a step-up token (Sec-H3 — body is ``BulkPermissionsPatchStepUpRequired``).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkPermissionsPatchStepUpRequired"}}}}}}},"/v1/enterprise/permission-rollout-status":{"get":{"tags":["Enterprise Permission Rollout Status (B-FE-10)"],"summary":"Permission rollout state for the FE-10 banner (B-FE-10)","description":"Return the caller's tenant rollout-status snapshot.\n\nResolution order (the four phases the FE banner branches on):\n\n1. Feature flag — return 404 when\n   ``Settings.permission_rollout_status_visible`` is False so the\n   banner hides without a frontend release.\n2. ``permission_filter_fail_open_legacy == False`` → ``post_flip``.\n   Strict-mode is live; this supersedes any in-progress backfill\n   state since the actual switch is what the banner is reporting on.\n3. Inspect ``permission_backfill_progress`` rows for the tenant\n   (``run_id IS NULL`` — Phase 4 nightly rows only). Derive a\n   per-row state from ``completed_at`` + ``last_error``:\n\n   * Every row completed AND ``pieces_processed >= pieces_total``\n     (or ``pieces_total IS NULL``, which Phase 4 leaves NULL\n     because the runner walks until exhaustion) →\n     ``backfill_complete``.\n   * Any row still ``in_progress`` (no completed_at, no error)\n     OR any row completed but ``pieces_processed < pieces_total``\n     → ``backfill_in_progress``. ``progress_pct`` is the row\n     average of ``pieces_processed / pieces_total``.\n   * No rows OR every row is errored / no recent advance →\n     ``not_started``.\n\n4. If the resolved phase is ``backfill_complete`` AND the operator\n   has set ``Settings.phase_5_flip_date_iso``, promote to\n   ``pre_flip`` and surface the flip date on the response.\n\nResult is cached per-tenant for 30 seconds (see module docstring\nfor rationale).","operationId":"get_permission_rollout_status_v1_enterprise_permission_rollout_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PermissionRolloutStatusResponse"}}}},"404":{"description":"Endpoint hidden (``Settings.permission_rollout_status_visible = False``). The FE banner treats 404 as 'hide gracefully'."}}}},"/v1/enterprise/permission-backfill/my-status":{"get":{"tags":["Enterprise Permission Backfill — Tenant (B-FE-7)"],"summary":"Tenant-scoped backfill status for the FE-9 dashboard","description":"Return the caller's tenant's permission-backfill status — phase, pieces processed / Qdrant-updated, percent complete, and a per-connector breakdown. The tenant_id is ALWAYS the resolved session principal's tenant; the endpoint does NOT accept a tenant id from the URL or query string.\n\nFrontend contract for B-FE-7 / FE-9: response shape is stable. See ``MyBackfillStatusResponse`` for the phase machine.\n\nAuth: ``RequiredRole.ADMIN`` (tenant admin); API keys denied.","operationId":"get_my_backfill_status_v1_enterprise_permission_backfill_my_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyBackfillStatusResponse"}}}}}}},"/v1/enterprise/permission-backfill/my-audit-tail":{"get":{"tags":["Enterprise Permission Backfill — Tenant (B-FE-7)"],"summary":"Tenant-scoped audit tail for the FE-9 dashboard","description":"Return the most recent ``permission_audit_log`` rows for the caller's tenant, ordered ``mutated_at DESC``. The tenant_id is ALWAYS the resolved session principal's tenant — the endpoint does NOT accept a tenant id from the URL or query string.\n\nPagination: ``limit`` defaults to 50, capped at 100 (``defence-in-depth`` — protect Postgres from accidental large scans by browser polls). The admin variant under ``/v1/admin/permission-backfill/audit-tail`` allows up to 500 rows for the operator audience.\n\nAuth: ``RequiredRole.ADMIN`` (tenant admin); API keys denied.","operationId":"get_my_audit_tail_v1_enterprise_permission_backfill_my_audit_tail_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max rows returned. Default 50, max 100 — the operator (``/v1/admin/...``) endpoint allows up to 500.","default":50,"title":"Limit"},"description":"Max rows returned. Default 50, max 100 — the operator (``/v1/admin/...``) endpoint allows up to 500."},{"name":"since","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"description":"Optional ISO 8601 lower bound on ``mutated_at`` (inclusive). Omit for no bound — caller sees the most-recent ``limit`` rows.","title":"Since"},"description":"Optional ISO 8601 lower bound on ``mutated_at`` (inclusive). Omit for no bound — caller sees the most-recent ``limit`` rows."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MyAuditTailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/threads":{"post":{"tags":["Enterprise Conversations (W8)"],"summary":"Create Thread","description":"Mint a fresh thread.\n\nRare path — the typical flow lazy-creates threads on the first\nstreaming query. Useful for clients that want to pre-mint a\nthread before issuing the first query (e.g., the W7 stop-button\nUX may need a known thread_id before stream start).","operationId":"create_thread_v1_enterprise_threads_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateThreadRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Enterprise Conversations (W8)"],"summary":"List Threads","description":"Paginated sidebar list — newest first, bucketed by recency.","operationId":"list_threads_v1_enterprise_threads_get","parameters":[{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 last_query_at","title":"Cursor"},"description":"ISO 8601 last_query_at"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListThreadsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/threads/{thread_id}":{"get":{"tags":["Enterprise Conversations (W8)"],"summary":"Get Thread","description":"Restore a thread + its messages in chronological order.","operationId":"get_thread_v1_enterprise_threads__thread_id__get","parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Thread Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 created_at","title":"Cursor"},"description":"ISO 8601 created_at"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Enterprise Conversations (W8)"],"summary":"Update Thread","description":"Rename and/or (un)pin a thread.\n\nAt least one of ``title`` or ``is_pinned`` must be provided.\nReturns 409 when pin transition would exceed the 100-pin cap.","operationId":"update_thread_v1_enterprise_threads__thread_id__patch","parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Thread Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateThreadRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ThreadCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Enterprise Conversations (W8)"],"summary":"Delete Thread","description":"Soft-delete a thread. Hidden from listings; physical deletion\nhappens via the cleanup cron after a grace window.","operationId":"delete_thread_v1_enterprise_threads__thread_id__delete","parameters":[{"name":"thread_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Thread Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content":{"post":{"tags":["Enterprise Upload"],"summary":"Upload Content","description":"Upload a single file for enterprise content ingestion.\n\nAccepts multipart/form-data with a file and metadata fields. The file\nis uploaded to S3 under the tenant's prefix and enqueued for enrichment.","operationId":"upload_content_v1_enterprise_content_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_upload_content_v1_enterprise_content_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SingleUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content/bulk":{"post":{"tags":["Enterprise Upload"],"summary":"Bulk Upload","description":"Bulk import content from S3 URIs (up to 100 items per request).\n\nEach item references an existing S3 object. The enrichment pipeline\nprocesses them asynchronously.","operationId":"bulk_upload_v1_enterprise_content_bulk_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content/upload-url":{"post":{"tags":["Enterprise Upload"],"summary":"Get Upload Url","description":"Generate a presigned S3 PUT URL for direct client upload.\n\nThe client uploads the file directly to S3 using the returned URL,\nthen calls POST /v1/enterprise/content/confirm/{content_piece_id}\nto trigger enrichment.","operationId":"get_upload_url_v1_enterprise_content_upload_url_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedUrlRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignedUrlResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/content/confirm/{content_piece_id}":{"post":{"tags":["Enterprise Upload"],"summary":"Confirm Upload","description":"Confirm that a presigned-URL upload completed.\n\nVerifies the file exists in S3, updates the content piece status,\ncommits the bridge row, then enqueues enrichment processing.","operationId":"confirm_upload_v1_enterprise_content_confirm__content_piece_id__post","parameters":[{"name":"content_piece_id","in":"path","required":true,"schema":{"type":"string","title":"Content Piece Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/tier-limits":{"get":{"tags":["Enterprise Upload"],"summary":"Get My Tier Limits","description":"Return the caller's tier ceiling + the upgrade ladder.\n\nBody shape (v_tier_file_lim001 contract — frontend consumes this\nverbatim):\n\n    {\n      \"tier\": \"free\",\n      \"max_file_size_bytes\": 104857600,\n      \"blocked_format_buckets\": [\"audio\", \"video\"],\n      \"upgrade_tiers\": [\n        {\"tier\": \"team\", \"max_file_size_bytes\": 1073741824,\n         \"blocked_format_buckets\": []},\n        ...\n      ]\n    }\n\n``max_file_size_bytes`` is ``null`` for unlimited (Enterprise).\n``upgrade_tiers`` is ordered from least- to most-permissive and\nexcludes the caller's current tier; for an Enterprise tenant it is\nempty.","operationId":"get_my_tier_limits_v1_enterprise_me_tier_limits_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Get My Tier Limits V1 Enterprise Me Tier Limits Get"}}}}}}},"/v1/enterprise/uploads/presign":{"post":{"tags":["Enterprise Multipart Upload (W18)"],"summary":"Presign Multipart Upload","description":"Start a multipart upload and return per-part presigned PUT URLs.\n\nThe frontend uploads each part directly to S3 using these URLs,\nbypassing Cloudflare and ALB. When all parts are uploaded, the\nfrontend calls ``/uploads/complete``.","operationId":"presign_multipart_upload_v1_enterprise_uploads_presign_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PresignUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/uploads/complete":{"post":{"tags":["Enterprise Multipart Upload (W18)"],"summary":"Complete Multipart Upload","description":"Finalize a multipart upload after all parts are in S3.\n\nVerifies the file exists, creates the bridge ContentPiece, and\npublishes to the enrichment queue. Post-conditions match the\nsynchronous direct-upload path.","operationId":"complete_multipart_upload_v1_enterprise_uploads_complete_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/uploads/{piece_id}/parts":{"post":{"tags":["Enterprise Multipart Upload (W18)"],"summary":"List Multipart Upload Parts","description":"List parts already uploaded for an in-progress multipart upload.\n\nUsed by the frontend resume flow on page reload — the IndexedDB\nstash holds the upload_id; this endpoint tells the frontend\nwhich part numbers are already in S3 so it can skip them.","operationId":"list_multipart_upload_parts_v1_enterprise_uploads__piece_id__parts_post","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","title":"Piece Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPartsRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPartsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/uploads/{piece_id}/abort":{"post":{"tags":["Enterprise Multipart Upload (W18)"],"summary":"Abort Multipart Upload","description":"Cancel an in-progress multipart upload.\n\nIdempotent — safe to call on an already-aborted upload. The S3\nlifecycle rule garbage-collects orphaned parts after 7 days,\nand the daily abort_stale_multipart_uploads.py cron catches\nanything older than 24h.","operationId":"abort_multipart_upload_v1_enterprise_uploads__piece_id__abort_post","parameters":[{"name":"piece_id","in":"path","required":true,"schema":{"type":"string","title":"Piece Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortUploadRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AbortUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/knowledge-map":{"get":{"tags":["Enterprise Intelligence"],"summary":"Knowledge Map","description":"Return domain coverage data for the knowledge map treemap (EP-003).\n\nViewer scoping (``feat/viewer-scoped-counts``): the underlying\nservice applies the same row-level ACL filter the Library list\nendpoint uses, so the per-domain document_count and the org_health\ntotals reflect \"documents the viewer is allowed to see\". This makes\nthe Knowledge Map's \"Currently mapping N documents\" header a subset\nof the Library's DOCUMENTS count, not the tenant-wide total.","operationId":"knowledge_map_v1_enterprise_knowledge_map_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KnowledgeMapResponse"}}}}}}},"/v1/enterprise/knowledge-map/{domain}":{"get":{"tags":["Enterprise Intelligence"],"summary":"Knowledge Map Domain","description":"Drill-down for a single taxonomy domain (EP-003 Layer 2).","operationId":"knowledge_map_domain_v1_enterprise_knowledge_map__domain__get","parameters":[{"name":"domain","in":"path","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/experts":{"get":{"tags":["Enterprise Intelligence"],"summary":"List Experts","description":"Find experts for a tenant, optionally by domain (EP-004).","operationId":"list_experts_v1_enterprise_intelligence_experts_get","parameters":[{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by taxonomy domain","title":"Domain"},"description":"Filter by taxonomy domain"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/expert-graph":{"get":{"tags":["Enterprise Intelligence"],"summary":"Expert Graph","description":"Expert collaboration graph — nodes + edges (EP-003 Layer 3).","operationId":"expert_graph_v1_enterprise_intelligence_expert_graph_get","parameters":[{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by taxonomy domain","title":"Domain"},"description":"Filter by taxonomy domain"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExpertGraphResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/health":{"get":{"tags":["Enterprise Intelligence"],"summary":"Health Scores","description":"Precomputed health scores from nightly cron (EP-005).","operationId":"health_scores_v1_enterprise_intelligence_health_get","parameters":[{"name":"scope_type","in":"query","required":false,"schema":{"type":"string","description":"organization | department | team","default":"organization","title":"Scope Type"},"description":"organization | department | team"},{"name":"scope_value","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to specific department/team name","title":"Scope Value"},"description":"Filter to specific department/team name"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthScoreResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/spof-risks":{"get":{"tags":["Enterprise Intelligence"],"summary":"Spof Risks","description":"Detect domains with single-expert dependency (EP-006).","operationId":"spof_risks_v1_enterprise_intelligence_spof_risks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SPOFListResponse"}}}}}}},"/v1/enterprise/intelligence/departure":{"post":{"tags":["Enterprise Intelligence"],"summary":"Flag Departure","description":"Flag an employee as departing and auto-generate transfer plans (EP-006).\n\nRestricted to manager and executive personas — ICs cannot flag departures.","operationId":"flag_departure_v1_enterprise_intelligence_departure_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartureFlagRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DepartureFlagResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/signals":{"get":{"tags":["Enterprise Intelligence"],"summary":"List Signals","description":"Return persona-filtered intelligence signals (EP-008, CLAUDE.md #40/#41).\n\nOnly signals whose ``persona_targets`` JSON array contains the employee's\npersona bucket are returned. Persona is detected from SSO claims at auth\ntime (CLAUDE.md #41).","operationId":"list_signals_v1_enterprise_signals_get","parameters":[{"name":"severity","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by severity: critical | warning | info","title":"Severity"},"description":"Filter by severity: critical | warning | info"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":20,"title":"Limit"}},{"name":"include_read","in":"query","required":false,"schema":{"type":"boolean","description":"Include already-read signals","default":false,"title":"Include Read"},"description":"Include already-read signals"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/signals/{signal_id}/read":{"post":{"tags":["Enterprise Intelligence"],"summary":"Mark Signal Read","description":"Mark an intelligence signal as read.","operationId":"mark_signal_read_v1_enterprise_signals__signal_id__read_post","parameters":[{"name":"signal_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Signal Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/people":{"get":{"tags":["Enterprise Intelligence"],"summary":"People Intelligence","description":"Live People Intelligence summary — stats + per-expert profiles.\n\nComputed at request time (not from the nightly cron) so the dashboard\nreflects current state immediately after content is ingested.","operationId":"people_intelligence_v1_enterprise_intelligence_people_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PeopleIntelligenceResponse"}}}}}}},"/v1/enterprise/intelligence/signals":{"get":{"tags":["Enterprise Intelligence"],"summary":"Signals Intelligence","description":"Live Signals dashboard payload — stats + auto-generated feed.\n\nGenerates intelligence items from current data without waiting for the\nnightly cron: single-expert risks, knowledge gaps, recent indexing.","operationId":"signals_intelligence_v1_enterprise_intelligence_signals_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalsIntelligenceResponse"}}}}}}},"/v1/enterprise/intelligence/timeline":{"get":{"tags":["Enterprise Intelligence"],"summary":"Intelligence Timeline","description":"K2 Intelligence Timeline — chronological list of topic-ranked chunks.\n\nv2 (2026-06-02). Given a topic, retrieves the top-ranked chunks\nvia hybrid_search and returns them sorted oldest → newest.\nCross-reference edges between adjacent rows are surfaced as a\nper-row highlight (``TimelineNode.edge_to_previous``) — they no\nlonger gate whether the timeline renders.\n\n**Auth:** SSO Bearer token via ``get_enterprise_auth``. Tenant ID\nis derived from the authenticated ``EmployeeContext`` — never from\na query param. This matches every other Intelligence-tab endpoint\nand prevents cross-tenant scope leakage that a query-param tenant\nwould expose.\n\n**Empty state:** the timeline is empty (``total_nodes=0``) only\nwhen the topic produced zero hybrid_search hits, or when one of\nthe earlier short-circuits fires (empty topic, missing tenant,\nembedding failure). The frontend renders its empty state in that\nsingle case — there is no longer a \"fewer than 2 connected\nsources\" empty path.\n\n**Pricing:** No Knowledge Tokens charged. Timeline is a read-only\nintelligence feature — zero billing, zero attribution events.\nMatches the treatment of ``/knowledge-map`` and the other\nIntelligence endpoints.","operationId":"intelligence_timeline_v1_enterprise_intelligence_timeline_get","parameters":[{"name":"topic","in":"query","required":true,"schema":{"type":"string","minLength":1,"maxLength":2000,"description":"Free-text topic to build the timeline for. Empty/whitespace returns an empty timeline without hitting the embedding service.","title":"Topic"},"description":"Free-text topic to build the timeline for. Empty/whitespace returns an empty timeline without hitting the embedding service."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TimelineResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/domains":{"get":{"tags":["Enterprise Intelligence"],"summary":"List Domains","description":"List taxonomy domains with coverage levels for the tenant.\n\nPowers the MCP `list_domains` tool — agent-facing discovery so a\nmodel can decide whether to spend a Knowledge Token on a query.\nTenant-scope; per-employee filtering ships with the SSO + Identity\nworkstream in v0.9.x.\n\nReuses the knowledge-map service so MCP and dashboard see the same\nnumbers. Skips the org-health aggregation that the dashboard\ntreemap needs but agents do not.\n\n**Pricing:** zero Knowledge Tokens — discovery is free.","operationId":"list_domains_v1_enterprise_domains_get","parameters":[{"name":"coverage","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional comma-separated subset of expert,partial,none. Default: all three. Unknown tokens 400.","title":"Coverage"},"description":"Optional comma-separated subset of expert,partial,none. Default: all three. Unknown tokens 400."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainsListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/sources/{query_id}":{"get":{"tags":["Enterprise Intelligence"],"summary":"Get Source Detail","description":"Return per-chunk provenance for a previously-executed query.\n\nPowers the MCP `get_source_detail` tool. Lets agents verify a\ncitation, surface lifecycle state to a downstream system, or cite\nthe canonical document path back to a human.\n\n**404 cases (indistinguishable from each other — never leak which\ncase fired):**\n  - query_id does not exist in any tenant\n  - query_id exists but for a different tenant\n  - query_id exists in this tenant but for a different employee\n    and the caller is not Owner/Admin\n  - row predates v0.7 provenance invariant (`returned_chunk_ids` NULL)\n\n**Within-tenant Layer 3 isolation (v0.8.8 foundation):** non-admin\ncallers can only inspect their own ``query_log`` rows. Legacy rows\nwritten before this column shipped (``employee_id IS NULL``) remain\ninspectable for backwards compatibility. Owner/Admin see every row\nin the tenant. See docs/v088-foundation-design.md §2.2.\n\n**Pricing:** zero Knowledge Tokens — provenance lookup is free.","operationId":"get_source_detail_v1_enterprise_sources__query_id__get","parameters":[{"name":"query_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Query Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/intelligence/relationships":{"get":{"tags":["Enterprise Intelligence"],"summary":"Intelligence Relationships","description":"Browse the cross-reference edge graph for the authenticated tenant.\n\nPowers the Intelligence → Relationships tab. The endpoint is\nstrictly tenant-scoped: ``tenant_id`` is read from the\nauthenticated session and NEVER from a request parameter (per the\nTenant-Scope Review Gate in CLAUDE.md).\n\n**Pricing:** zero Knowledge Tokens — discovery is free.","operationId":"intelligence_relationships_v1_enterprise_intelligence_relationships_get","parameters":[{"name":"edge_types","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter chips. Each value is one of agreement|disagreement|complementary|superseding. Omitted or empty returns all four.","title":"Edge Types"},"description":"Filter chips. Each value is one of agreement|disagreement|complementary|superseding. Omitted or empty returns all four."},{"name":"domain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Taxonomy domain — at least one edge endpoint must match.","title":"Domain"},"description":"Taxonomy domain — at least one edge endpoint must match."},{"name":"subdomain","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Taxonomy subdomain — accepted for forward compatibility; currently a no-op (no subdomain column on enterprise pieces yet).","title":"Subdomain"},"description":"Taxonomy subdomain — accepted for forward compatibility; currently a no-op (no subdomain column on enterprise pieces yet)."},{"name":"time_range_days","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":3650,"minimum":1},{"type":"null"}],"description":"Only return edges created within the last N days.","title":"Time Range Days"},"description":"Only return edges created within the last N days."},{"name":"topic","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"description":"Free-text topic. Runs hybrid_search against the tenant's Qdrant collection and restricts edges to those touching the top-K seed chunks. Empty seed → empty response.","title":"Topic"},"description":"Free-text topic. Runs hybrid_search against the tenant's Qdrant collection and restricts edges to those touching the top-K seed chunks. Empty seed → empty response."},{"name":"min_confidence","in":"query","required":false,"schema":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"description":"Lower bound on edge confidence. NULL-confidence edges are included only when min_confidence <= 0 (no minimum).","title":"Min Confidence"},"description":"Lower bound on edge confidence. NULL-confidence edges are included only when min_confidence <= 0 (no minimum)."},{"name":"sort","in":"query","required":false,"schema":{"type":"string","description":"One of recent | authority | confidence.","default":"recent","title":"Sort"},"description":"One of recent | authority | confidence."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Page size (1..100).","default":25,"title":"Limit"},"description":"Page size (1..100)."},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Opaque pagination cursor returned from the previous response's `next_cursor`. Only honoured for sort=recent.","title":"Cursor"},"description":"Opaque pagination cursor returned from the previous response's `next_cursor`. Only honoured for sort=recent."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RelationshipsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/search/filter-presets":{"get":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"List Filter Presets","description":"List presets the caller can see (own personal + tenant-published).","operationId":"list_filter_presets_v1_enterprise_search_filter_presets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetListResponse"}}}}}},"post":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Create Filter Preset","operationId":"create_filter_preset_v1_enterprise_search_filter_presets_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateFilterPresetRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/search/filter-presets/{preset_id}":{"get":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Get Filter Preset","operationId":"get_filter_preset_v1_enterprise_search_filter_presets__preset_id__get","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Update Filter Preset","operationId":"update_filter_preset_v1_enterprise_search_filter_presets__preset_id__patch","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateFilterPresetRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Delete Filter Preset","operationId":"delete_filter_preset_v1_enterprise_search_filter_presets__preset_id__delete","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/search/filter-presets/{preset_id}/publish":{"post":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Publish Filter Preset","operationId":"publish_filter_preset_v1_enterprise_search_filter_presets__preset_id__publish_post","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/search/filter-presets/{preset_id}/unpublish":{"post":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Unpublish Filter Preset","operationId":"unpublish_filter_preset_v1_enterprise_search_filter_presets__preset_id__unpublish_post","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/search/filter-presets/{preset_id}/apply":{"post":{"tags":["Enterprise Filter Presets (v0.9.1)"],"summary":"Apply Filter Preset","description":"Record an apply (use_count++, last_used_at=NOW()).\n\nThe actual filter is applied at query time by sending the preset's\n``mode`` / ``source_ids`` / ``piece_ids`` to /v1/enterprise/query —\nthis endpoint is just the analytics ping. Visibility-gated, so\nmembers cannot bump counters on presets they cannot see.","operationId":"apply_filter_preset_v1_enterprise_search_filter_presets__preset_id__apply_post","parameters":[{"name":"preset_id","in":"path","required":true,"schema":{"type":"string","title":"Preset Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FilterPresetSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sources/tree":{"get":{"tags":["Enterprise Sources Tree (W6)"],"summary":"List all sources for the tenant with piece counts (W6 source filter)","description":"Return a flat list of sources for the current tenant.\n\nCached ~60s in Redis. Cache key includes the max ``last_sync_at`` across\nthe tenant's connectors so a freshly synced source invalidates the cache\nnaturally (newer max → different key). Cache miss is cheap — single\ngrouped COUNT, sub-10ms on a tenant with hundreds of connectors.\n\nViewer scoping (``feat/viewer-scoped-counts``): the per-source\n``piece_count`` is gated by the same row-level ACL\n(``library_permission_predicate``) the Library list endpoint\napplies. Two consequences:\n\n  * The numbers in the Sources tree are a subset of the viewer's\n    Content Library count — they reflect \"files visible to me from\n    this connector\", not the tenant-wide ingestion count.\n  * The cache key is partitioned by the viewer's principal\n    (lowercased email + sorted group list) so two users in the\n    same tenant don't share a cached payload with each other's\n    counts.\n\nPer-piece permission filtering still runs at retrieval time (G4)\nvia ``hybrid_search.py``; this filter brings the cosmetic count on\nthe tree into line with that boundary.","operationId":"get_sources_tree_v1_sources_tree_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SourceTreeResponse"}}}}}}},"/v1/sources/tree/{source_id}/pieces":{"get":{"tags":["Enterprise Sources Tree (W6)"],"summary":"List pieces under a source (W6 source filter, lazy-load)","description":"Return one page of pieces under the given source.\n\nTenant isolation is enforced by joining on ``connector.tenant_id ==\nemployee.tenant_id`` — a user requesting a source that doesn't belong\nto them gets an empty result, not a 404 leak about other tenants'\nsources.","operationId":"get_source_pieces_v1_sources_tree__source_id__pieces_get","parameters":[{"name":"source_id","in":"path","required":true,"schema":{"type":"string","title":"Source Id"}},{"name":"cursor","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Keyset pagination cursor","title":"Cursor"},"description":"Keyset pagination cursor"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"description":"Max pieces per page (capped server-side at 500).","default":200,"title":"Limit"},"description":"Max pieces per page (capped server-side at 500)."},{"name":"q","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"description":"Server-side typeahead substring on title (case-insensitive). Empty/None = no filter. Q3 ruling: server-side always.","title":"Q"},"description":"Server-side typeahead substring on title (case-insensitive). Empty/None = no filter. Q3 ruling: server-side always."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TreePiecesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/invites":{"get":{"tags":["Enterprise Invites"],"summary":"List Invites Route","description":"List active (non-revoked, non-expired, non-exhausted) invites.","operationId":"list_invites_route_v1_enterprise_invites_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListInvitesResponse"}}}}}},"post":{"tags":["Enterprise Invites"],"summary":"Create Invite Route","description":"Create a new workspace invite token.\n\nAuthorization: admin and manager always allowed. Members allowed\niff ``tenant.allow_members_invite`` is True (see Workspace settings).\n\nThe response includes the raw token and a shareable URL — both are\nshown exactly once. Subsequent list calls return the invite without\nthe token.","operationId":"create_invite_route_v1_enterprise_invites_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInviteRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateInviteResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/invites/{invite_id}":{"delete":{"tags":["Enterprise Invites"],"summary":"Revoke Invite Route","description":"Soft-revoke an invite. Idempotent — already-revoked returns 204.","operationId":"revoke_invite_route_v1_enterprise_invites__invite_id__delete","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invite Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/invites/{invite_id}/resend":{"post":{"tags":["Enterprise Invites"],"summary":"Resend Invite Route","description":"Re-dispatch the initial invite email and clear ``rejected_at``.\n\nAdmin-only — same role gate as DELETE /invites/{id}. The response\nreturns the updated ``InviteSummary`` (with the recomputed\n``status``) so the frontend can update the row without a follow-up\nlist fetch.\n\nStatus codes:\n    200 — re-dispatched (email delivery is best-effort; admin sees\n          the new ``status`` + bumped ``resend_count`` regardless of\n          the underlying send outcome)\n    403 — caller is not admin / used an API key\n    404 — invite_id not found OR belongs to a different tenant\n    409 — invite is in a closed state (``accepted`` or ``revoked``)\n    422 — invite has no ``email_hint`` (link-only invite — nothing\n          to send to)","operationId":"resend_invite_route_v1_enterprise_invites__invite_id__resend_post","parameters":[{"name":"invite_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Invite Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InviteSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/invites/accept/{token}":{"get":{"tags":["Enterprise Invites"],"summary":"Get Invite Metadata Route","description":"Public metadata lookup for the invite-accept page.","operationId":"get_invite_metadata_route_v1_enterprise_invites_accept__token__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/InviteMetadataResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Enterprise Invites"],"summary":"Accept Invite Route","description":"Redeem an invite token. Idempotent for existing members.\n\nRate-limited to ``rate_limit_invite_accept_per_min`` per IP by the\nmiddleware in ``common/infrastructure/middleware/rate_limiter.py``.","operationId":"accept_invite_route_v1_enterprise_invites_accept__token__post","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MembershipResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/onboarding/create-org":{"post":{"tags":["Enterprise Invites"],"summary":"Create Org Route","description":"Self-service tenant creation for first-time Clerk signups (W16).\n\nAuth: Clerk session only (``get_clerk_identity_only``) — by design\nthe caller does NOT yet belong to any tenant. The action derives\n``email_domain`` from the verified Clerk email and creates a Tenant\nplus the first TenantMember (role=admin). 409 on domain conflict.","operationId":"create_org_route_v1_enterprise_onboarding_create_org_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/api-keys":{"post":{"tags":["Enterprise API Keys (W14)"],"summary":"Mint Api Key","description":"Mint a new enterprise API key.\n\nReturns the full plaintext key in ``key`` ONCE. Save it on the\nspot — the key is not retrievable afterward.\n\nReturns 429 with ``key_quota_exceeded`` if the tenant already has\n20 active (non-revoked) keys (CEO ruling W14 Q5).","operationId":"mint_api_key_v1_enterprise_api_keys_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintApiKeyRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintApiKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Enterprise API Keys (W14)"],"summary":"List Api Keys","description":"List the tenant's API keys (sanitized — prefix only, never the\nfull key).\n\nDefault returns active keys only. Pass ``?include_revoked=true`` to\nsurface revoked rows in chronological order behind the active ones.","operationId":"list_api_keys_v1_enterprise_api_keys_get","parameters":[{"name":"include_revoked","in":"query","required":false,"schema":{"type":"boolean","description":"Include revoked keys in the response (for the 'Revoked' tab in the dashboard). Default false — active keys only.","default":false,"title":"Include Revoked"},"description":"Include revoked keys in the response (for the 'Revoked' tab in the dashboard). Default false — active keys only."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListApiKeysResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/api-keys/{key_id}/revoke":{"post":{"tags":["Enterprise API Keys (W14)"],"summary":"Revoke Api Key","description":"Revoke an API key. Effective immediately — the next request that\nuses this key fails with 401.\n\nCross-tenant safe: the action filters by ``tenant_id`` so a revoke\nagainst a key from another tenant returns 404, not 200.","operationId":"revoke_api_key_v1_enterprise_api_keys__key_id__revoke_post","parameters":[{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeApiKeyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeApiKeyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/personal-access-tokens":{"get":{"tags":["Personal Access Tokens (CLI Phase 1)"],"summary":"List the calling user's Personal Access Tokens","description":"List the calling user's Personal Access Tokens (active only, sanitized). Each token is returned with metadata plus the 16-char ``key_prefix`` for identification — never the full plaintext key or the bcrypt hash.\n\n**Auth:** dashboard SSO only (API keys denied).\n\nCross-user isolation: only the calling user's PATs appear. Other members' PATs are not reachable through this endpoint.","operationId":"list_personal_access_tokens_v1_enterprise_me_personal_access_tokens_get","responses":{"200":{"description":"Sanitized list of the user's active PATs.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListPatsResponse"},"example":{"tokens":[{"id":"8f3c6c11-2c5a-4f0d-9c1b-1e0a2c1d3b40","name":"Laptop","key_prefix":"qlv_pat_a1b2c3d4","created_at":"2026-05-21T10:15:00Z","expires_at":"2027-05-21T10:15:00Z","last_used_at":"2026-05-21T12:00:00Z"}]}}}}}},"post":{"tags":["Personal Access Tokens (CLI Phase 1)"],"summary":"Mint a Personal Access Token (CLI auth)","description":"Mint a new Personal Access Token for the calling user. The full plaintext key is returned ONCE in the ``key`` field of the response — save it on the spot; it cannot be re-displayed. Subsequent ``GET`` calls return only the metadata + 16-char ``key_prefix`` for identification.\n\n**Uniqueness:** ``name`` must be unique among the user's non-revoked tokens. Re-using a name after revocation is allowed. A collision returns 409.\n\n**Auth:** dashboard SSO only — privilege-escalation prevention. A PAT cannot mint another PAT (returns 403).\n\n**Audit:** writes a ``pat.created`` row to ``audit_log``.","operationId":"create_personal_access_token_v1_enterprise_me_personal_access_tokens_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintPatRequest"}}},"required":true},"responses":{"201":{"description":"PAT minted. ``key`` shown once.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MintPatResponse"},"example":{"id":"8f3c6c11-2c5a-4f0d-9c1b-1e0a2c1d3b40","name":"Laptop","key_prefix":"qlv_pat_a1b2c3d4","created_at":"2026-05-21T10:15:00Z","expires_at":"2027-05-21T10:15:00Z","key":"qlv_pat_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789-_aBcDeFg","warning":"Save this token now. It will not be shown again."}}}},"409":{"description":"A non-revoked token with this name already exists.","content":{"application/json":{"example":{"detail":"A personal access token named 'Laptop' already exists. Revoke it or pick a different name."}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/personal-access-tokens/{pat_id}":{"delete":{"tags":["Personal Access Tokens (CLI Phase 1)"],"summary":"Revoke a Personal Access Token","description":"Revoke a Personal Access Token owned by the calling user. Effective immediately — the next request using this PAT fails with 401. Soft-delete only: the row remains for audit-trail reachability via ``audit_log.resource_id``.\n\n**Cross-user isolation:** if ``pat_id`` does not belong to the calling user, the response is 404 — never 200 or 403 — to avoid leaking the existence of another user's PAT.\n\n**Auth:** dashboard SSO only.\n\n**Audit:** writes a ``pat.revoked`` row to ``audit_log``.","operationId":"revoke_personal_access_token_v1_enterprise_me_personal_access_tokens__pat_id__delete","parameters":[{"name":"pat_id","in":"path","required":true,"schema":{"type":"string","title":"Pat Id"}}],"responses":{"204":{"description":"Successful Response"},"404":{"description":"No active token with this id is owned by the calling user. Same response whether the token does not exist, belongs to another user, or was already revoked."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/memory":{"get":{"tags":["User Brain Memory (Settings → Personalization)"],"summary":"Get the brain's user-facing view of the caller","description":"Returns the caller's user memory and a transparency slice of tenant memory, framed for user readability. Raw classifier confidence scores are mapped to qualitative bands (established / emerging / tentative); raw scores never leave this endpoint.","operationId":"get_my_memory_v1_enterprise_me_memory_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMemoryResponse"}}}}}}},"/v1/enterprise/me/memory/sources":{"get":{"tags":["User Brain Memory (Settings → Personalization)"],"summary":"What contributed to the brain's view of the caller","description":"Returns the content pieces the caller authored and the queries they ran in the last 30 days — the substrate the user-memory and individual-context layers learn from. Paginated.","operationId":"get_my_memory_sources_v1_enterprise_me_memory_sources_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSourcesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/memory/purge":{"post":{"tags":["User Brain Memory (Settings → Personalization)"],"summary":"Stop building memory + purge all derived facts","description":"Clears every entry in the caller's user_memory, deletes their user_event_significance rows, and removes their memory edit audit log entries. Sets ``memory_building_stopped=True`` so future events skip classification. Idempotent — calling twice succeeds both times.","operationId":"purge_my_memory_v1_enterprise_me_memory_purge_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PurgeResponse"}}}}}}},"/v1/enterprise/me/memory/resume-building":{"post":{"tags":["User Brain Memory (Settings → Personalization)"],"summary":"Resume building memory about the caller (inverse of purge stop)","description":"Flips ``user_memory.memory_building_stopped`` back to ``False`` for the caller's (user_id, tenant_id) row so the classifier and edit worker re-engage on future events. Does NOT restore data cleared by a previous /purge — that data is destroyed by design. Idempotent: calling twice on an already-resumed row returns 200 with rows_resumed=1 both times. A caller who has never built memory returns rows_resumed=0 (no row exists yet) — the first future event will lazily create the row with stopped=False.","operationId":"resume_my_memory_building_v1_enterprise_me_memory_resume_building_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResumeBuildingResponse"}}}}}}},"/v1/enterprise/me/memory/audit":{"get":{"tags":["User Brain Memory (Settings → Personalization)"],"summary":"Audit log of every edit to the caller's user_memory","description":"Returns paginated ``memory_edit_audit_log`` rows scoped to the caller. Each entry includes the operation, the affected field, the source event type, and the applied-by actor.","operationId":"get_my_memory_audit_v1_enterprise_me_memory_audit_get","parameters":[{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Page"}},{"name":"page_size","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":25,"title":"Page Size"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAuditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/tenants/{tenant_id}":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Full tenant brain-memory dump (operator view)","operationId":"get_tenant_memory_dump_v1_admin_memory_tenants__tenant_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantMemoryDumpResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/tenants/{tenant_id}/users":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Paginated list of users with user_memory rows in this tenant","description":"List user_memory rows for the tenant joined with tenant_members.\n\nLEFT JOIN on TenantMember — the user_memory row may outlive a\nmembership (member rows can be deleted; user_memory historical\npresence is retained for audit). Falling back to email=None,\ndisplay_name=None covers that edge.","operationId":"list_tenant_user_memories_v1_admin_memory_tenants__tenant_id__users_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TenantUsersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/tenants/{tenant_id}/users/{user_id}":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Full user_memory dump for one (tenant, user) pair","operationId":"get_user_memory_dump_v1_admin_memory_tenants__tenant_id__users__user_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"User Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserMemoryDumpResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/audit":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Paginated memory_edit_audit_log entries scoped to a tenant","operationId":"list_audit_entries_v1_admin_memory_audit_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"}},{"name":"memory_artifact","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Memory Artifact"}},{"name":"from_iso","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"From Iso"}},{"name":"to_iso","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"To Iso"}},{"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/AuditResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/classifier-feedback":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Paginated operator classifier-feedback votes","description":"List resolved classifier-feedback votes.\n\nThe schema enforces ``operator_classification NOT NULL``, so every\nrow in this table is \"resolved\" by definition. The \"show unresolved\"\nworkflow surfaces source rows that have no feedback yet — that's a\ndifferent query against content_pieces / user_event_significance,\nwhich the dispatch defers to a follow-up surface.\n\nFilters: ``tenant_id`` (operator scope-down), ``classifier_type``\n(which classifier — tenant_memory_significance or\nuser_event_significance).","operationId":"list_classifier_feedback_v1_admin_memory_classifier_feedback_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"}},{"name":"classifier_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Classifier Type"}},{"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/ClassifierFeedbackListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"Operator overrides a classifier verdict (write)","description":"Persist an operator override into ``memory_classifier_feedback``.\n\nThe ``tenant_id`` is derived server-side from the source row keyed\nby (classifier_type, source_id). The request body has no tenant_id\nfield — this prevents an operator (or a compromised admin key)\nfrom poisoning the training corpus with cross-tenant feedback.\n\n``operator_user_id`` is set to a stable synthetic UUID derived from\nthe AdminIdentity. v0.2 shared-secret auth has no per-user identity\n(see ``app/domains/admin/presentation/dependencies.py``); when v0.3+\nswaps in Clerk-derived per-user admin identity this column will\ncarry the real user UUID.","operationId":"submit_classifier_feedback_v1_admin_memory_classifier_feedback_post","security":[{"APIKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassifierFeedbackSubmitRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClassifierFeedbackSubmitResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/memory/classifier-feedback/unresolved":{"get":{"tags":["Admin · Brain Memory","Admin · Brain Memory"],"summary":"List classifier verdicts with no operator feedback yet","description":"Surface classifier verdicts the operator hasn't voted on yet.\n\nThe verdicts themselves are persisted at the source:\n\n  * tenant_memory_significance →\n    ``enterprise_content_pieces.tenant_memory_significance`` plus\n    its ``_confidence``, ``_classified_at``, ``_reasoning``\n    siblings. tenant_id is on the row directly.\n\n  * user_event_significance → ``user_event_significance.significance``\n    plus its ``_confidence``, ``classified_at``, ``reasoning``\n    siblings. tenant_id is on the row directly.\n\n\"Unresolved\" means the source row's ``id`` does not appear as\n``source_id`` in ``memory_classifier_feedback`` for the matching\nclassifier_type. We anti-join via NOT EXISTS so the planner can\nuse the existing source-id key.\n\nThe response shape is uniform across both classifier types so the\nUI can render a single voting queue.","operationId":"list_unresolved_classifier_verdicts_v1_admin_memory_classifier_feedback_unresolved_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","description":"Tenant scope (required).","title":"Tenant Id"},"description":"Tenant scope (required)."},{"name":"classifier_type","in":"query","required":true,"schema":{"type":"string","maxLength":40,"description":"Which classifier — tenant_memory_significance or user_event_significance.","title":"Classifier Type"},"description":"Which classifier — tenant_memory_significance or user_event_significance."},{"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/UnresolvedVerdictListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/sessions":{"get":{"tags":["Active Sessions (CLI Phase 7)"],"summary":"List the calling user's active OAuth sessions","description":"List the calling user's active OAuth sessions (browser + CLI). Sorted by ``last_used_at`` descending; never-used rows fall to the bottom. ``is_current`` is true only when the request itself arrived on an OAuth access token — a Clerk-session caller sees ``is_current=false`` on every row.\n\nCross-user isolation: only the calling user's sessions appear. Other members' sessions cannot be reached through this endpoint.","operationId":"list_sessions_v1_enterprise_me_sessions_get","responses":{"200":{"description":"Sanitized list of the calling user's sessions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListSessionsResponse"},"example":{"sessions":[{"id":"8f3c6c11-2c5a-4f0d-9c1b-1e0a2c1d3b40","client_label":"Chrome 130 on macOS","client_kind":"browser","created_at":"2026-05-22T10:15:00Z","last_used_at":"2026-05-22T12:00:00Z","is_current":false}]}}}}}}},"/v1/enterprise/me/sessions/{session_id}":{"delete":{"tags":["Active Sessions (CLI Phase 7)"],"summary":"Revoke an OAuth session owned by the calling user","description":"Revoke a single OAuth session owned by the calling user. Effective immediately — the next request using this session's access token fails with 401. Soft-delete only.\n\n**Cross-user isolation:** if ``session_id`` does not belong to the calling user, the response is 404 — never 200 or 403 — to avoid leaking the existence of another user's session.\n\n**Audit:** writes a ``session.revoked`` row to ``audit_log``.","operationId":"revoke_session_v1_enterprise_me_sessions__session_id__delete","parameters":[{"name":"session_id","in":"path","required":true,"schema":{"type":"string","title":"Session Id"}}],"responses":{"204":{"description":"Successful Response"},"404":{"description":"No active session with this id is owned by the calling user. Same response whether the session does not exist, belongs to another user, or was already revoked."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/me/sessions/revoke-others":{"post":{"tags":["Active Sessions (CLI Phase 7)"],"summary":"Sign out everywhere except this device","description":"Revoke every active OAuth session for the calling user EXCEPT the one matching this request's access token. When the caller is signed in via Clerk JWT (browser dashboard), there is no matching OAuth session and ALL active OAuth sessions are revoked.\n\n**Audit:** writes a single ``session.revoked_others`` row carrying the revoked count and the kept session id (when any).","operationId":"revoke_other_sessions_v1_enterprise_me_sessions_revoke_others_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RevokeOthersResponse"}}}}}}},"/v1/enterprise/me/telemetry":{"post":{"tags":["Observability (CLI Phase 14a)"],"summary":"Submit a CLI telemetry event (opt-in)","description":"Record one opt-in CLI lifecycle event. The CLI sends this when telemetry consent is enabled (Phase 14b). The body schema is **strict**: extra fields are rejected, no raw error messages are accepted, and ``error_message_hash`` must be a SHA-256 hex digest.\n\n**Rate limit:** 60 events per user per hour. The 61st event in a rolling hour returns 429 with ``Retry-After``.","operationId":"post_telemetry_v1_enterprise_me_telemetry_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetryEventBody"}}},"required":true},"responses":{"202":{"description":"Telemetry event stored.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetryAcceptedResponse"}}}},"422":{"description":"Payload validation failed (extra field, bad hash, etc.)."},"429":{"description":"Telemetry rate limit exceeded."}}}},"/v1/enterprise/me/activity":{"get":{"tags":["Observability (CLI Phase 14a)"],"summary":"List the calling user's own audit-log activity","description":"Paginated audit-log rows authored by the calling user. Filtered to ``employee_id == current``. Last 30 days by default; pass ``start_date`` / ``end_date`` (ISO-8601) to widen or narrow.\n\n**Cross-user isolation:** other members' rows are never returned. **Cross-tenant isolation:** rows from other tenants are never returned (the query filters on the authenticated tenant id, not a request-supplied value).","operationId":"list_my_activity_v1_enterprise_me_activity_get","parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"}},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"}},{"name":"client_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Kind"}},{"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/AuditListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/audit-log":{"get":{"tags":["Observability (CLI Phase 14a)"],"summary":"Tenant-wide audit log (admin-only)","description":"Paginated audit-log rows scoped to the caller's tenant. Returns rows ordered by ``created_at DESC``. Default page size 50; max 200.\n\n**Filters:** ``user_id`` (filters by ``employee_id``), ``action_type`` (filters by ``action``), ``client_kind``, ``command_name``, plus ``start_date`` / ``end_date`` (ISO-8601).\n\n**Auth:** admin (owner or admin) over dashboard SSO. Non-admins receive 403.","operationId":"list_admin_audit_log_v1_enterprise_admin_audit_log_get","parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"}},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"action_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Type"}},{"name":"client_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Kind"}},{"name":"command_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Command Name"}},{"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/AuditListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/audit-log/export":{"get":{"tags":["Observability (CLI Phase 14a)"],"summary":"Tenant-wide audit log CSV export (admin-only)","description":"Same filter shape as ``GET /admin/audit-log``. Returns ``text/csv`` with up to 10,000 rows ordered newest-first. Filename is suffixed with the export timestamp.\n\nAuth + tenant scoping identical to the JSON variant.","operationId":"export_admin_audit_log_v1_enterprise_admin_audit_log_export_get","parameters":[{"name":"start_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Start Date"}},{"name":"end_date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"End Date"}},{"name":"user_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"}},{"name":"action_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Type"}},{"name":"client_kind","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Kind"}},{"name":"command_name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Command Name"}}],"responses":{"200":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/telemetry-summary":{"get":{"tags":["Observability (CLI Phase 14a)"],"summary":"Aggregated CLI telemetry stats (admin-only)","description":"Returns CLI version distribution, top command names, and error class breakdown over a rolling ``window_days`` window (default 30, max 90). Counts are over the calling admin's tenant only.","operationId":"telemetry_summary_v1_enterprise_admin_telemetry_summary_get","parameters":[{"name":"window_days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"default":30,"title":"Window Days"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TelemetrySummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/security-policies":{"get":{"tags":["Tenant Security Policies (cli-p15a)"],"summary":"Get current tenant security policy","description":"Return the current tenant security policy. Tenants with no configured policy receive a payload with every field NULL — the canonical 'no enforcement' state, behaviorally identical to pre-Phase-15a.\n\nAdmin-only (``RequiredRole.ADMIN``); API keys denied.","operationId":"get_security_policy_v1_enterprise_admin_security_policies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecurityPolicyResponse"}}}}}},"put":{"tags":["Tenant Security Policies (cli-p15a)"],"summary":"Replace tenant security policy","description":"Replace the tenant security policy. Caller passes the full target state — every field independently nullable.\n\n**Self-lockout prevention:** when ``ip_allowlist_cidrs`` is set, the caller's current client IP must fall inside at least one CIDR in the proposed list. Otherwise the next request would 403 and the admin loses access to this endpoint to fix it. Returns 422 BEFORE persisting if the check fails.\n\nAdmin-only; API keys denied.","operationId":"update_security_policy_v1_enterprise_admin_security_policies_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSecurityPolicyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SecurityPolicyResponse"}}}},"422":{"description":"Validation failed: malformed CIDR, empty allowlist, or self-lockout."}}}},"/v1/enterprise/webhooks/offboarding":{"post":{"tags":["Tenant Offboarding · Webhook (cli-p15b)"],"summary":"HRIS offboarding webhook receiver (HMAC-signed)","description":"Accept an HRIS offboarding event signed via ``X-Quelvio-Signature``.\n\n**Authentication:** HMAC-SHA-256 only — no SSO, no API keys. The endpoint verifies the signature against the tenant's active webhook secrets and rejects with 401 on no match.\n\n**Idempotency:** duplicate bodies (same SHA-256 hash from the same tenant) return 200 with ``status='duplicate_ignored'`` instead of re-running revocation.\n\n**Fail-closed:** when the offboarding KEK is not configured, the endpoint returns 503 — the system cannot decrypt webhook secrets and refuses to ever verify a 'no secret available' match as success.","operationId":"receive_offboarding_webhook_v1_enterprise_webhooks_offboarding_post","responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OffboardingWebhookAck"}}}},"401":{"description":"Signature missing or invalid."},"503":{"description":"Webhook system not configured (KEK missing)."}}}},"/v1/enterprise/admin/offboard":{"post":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"Manually offboard a tenant member (admin-only)","description":"Run the same atomic revocation orchestrator that the HRIS webhook receiver triggers, but with the admin acting as the source. Admins MUST type the target email in the dashboard confirmation modal (UI-side guardrail) before this endpoint is called.\n\nManual events SKIP the payload-hash idempotency guard by design — the admin may need to retry after a transient failure. The orchestrator itself is idempotent (UPDATE WHERE revoked_at IS NULL), so the worst-case repeat is a zero-credential second run that logs cleanly.","operationId":"manual_offboard_v1_enterprise_admin_offboard_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualOffboardRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualOffboardResponse"}}}},"403":{"description":"Caller is not admin (or is a non-SSO identity)."},"404":{"description":"Target email is not a member of this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/offboarding-webhooks":{"get":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"List configured offboarding webhooks (admin-only)","operationId":"list_offboarding_webhooks_v1_enterprise_admin_offboarding_webhooks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookListResponse"}}}}}},"post":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"Create an offboarding webhook (admin-only)","operationId":"create_offboarding_webhook_v1_enterprise_admin_offboarding_webhooks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/offboarding-webhooks/{webhook_id}":{"patch":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"Update an offboarding webhook (admin-only)","description":"Toggle ``enabled``, rotate the HMAC secret, or both. When ``rotate_secret=True`` is set, the server mints a 32-byte URL-safe random secret and returns the plaintext ONCE in ``secret_once``. When ``new_secret`` is provided, that value is used verbatim (must be 16-512 chars). Passing both fields prefers ``new_secret``.","operationId":"update_offboarding_webhook_v1_enterprise_admin_offboarding_webhooks__webhook_id__patch","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookUpdatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"Soft-delete an offboarding webhook (admin-only)","description":"Flips ``enabled=False`` so the webhook receiver stops matching its signature, but preserves the row + history. Re-enable via PATCH ``enabled=True``. Returns the updated row summary (callers checking 204 should be relaxed to treat any 2xx as success).","operationId":"delete_offboarding_webhook_v1_enterprise_admin_offboarding_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"string","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookSummary"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/offboarding-events":{"get":{"tags":["Tenant Offboarding · Admin (cli-p15b)"],"summary":"List offboarding events (admin-only)","operationId":"list_offboarding_events_v1_enterprise_admin_offboarding_events_get","parameters":[{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"target_email","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Email"}},{"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/OffboardingEventListResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/service-accounts":{"get":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"List service accounts","description":"Return every service account in the authenticated tenant, ordered by creation time (newest first). Includes revoked accounts so admins can audit historical identities; clients may filter on `revoked_at` for an active-only view.","operationId":"list_service_accounts_v1_service_accounts_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceAccountsResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."}}},"post":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Create a service account","description":"Provision a new machine identity for the authenticated tenant. Service accounts have NO human user and explicitly BYPASS the per-employee `permission_emails` ACL filter at retrieval time — their access is governed entirely by the admin-defined `scopes` list. Empty `scopes` = deny-by-default (the account can read nothing until at least one scope entry is added).\n\nScope entry types (v1):\n* `domain` — taxonomy domain prefix (hierarchical, dot-separated)\n* `connector` — UUID of an EnterpriseConnector in this tenant\n* `path_prefix` — string prefix of `source_url`\n\nNo key is minted here. POST to `/v1/service-accounts/{id}/keys` to issue one.","operationId":"create_service_account_v1_service_accounts_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceAccountRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"400":{"description":"Scope validation failed (invalid type / value)."},"409":{"description":"Name already exists for this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/service-accounts/{account_id}":{"get":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Get service account detail","description":"Fetch a single service account by id, scoped to the authenticated tenant. Cross-tenant lookups return 404 without leaking existence.","operationId":"get_service_account_v1_service_accounts__account_id__get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Update service account","description":"Patch the name, description, or scopes of an existing service account. Scope changes emit a `service_account.scope_changed` audit event; name / description changes do not (consistent with the enterprise API key pattern). Any field can be omitted to leave it unchanged.","operationId":"update_service_account_v1_service_accounts__account_id__patch","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateServiceAccountRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"400":{"description":"Scope validation failed."},"409":{"description":"New name collides with an existing account."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Revoke an entire service account","description":"Soft-delete the account (sets `revoked_at`). The auth validator re-checks the account row on every request, so revoking immediately invalidates EVERY issued key even ones still in someone's environment. Returns 200 with the post-revoke row so clients can verify the timestamp.","operationId":"revoke_service_account_v1_service_accounts__account_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccountResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/service-accounts/{account_id}/keys":{"post":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Mint a new key","description":"Issue a new `qlv_svc_*` bearer key under an existing service account. The full plaintext is returned exactly once in the `key` field — save it on the spot; only the bcrypt hash is persisted. Pass `expires_in_days` for a wall-clock expiring key; omit for a durable key (matches `qlv_ent_*` defaults). Refuses to mint against a revoked account.","operationId":"create_service_account_key_v1_service_accounts__account_id__keys_post","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyRequest"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateKeyResponse"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"400":{"description":"`expires_in_days` must be a positive integer."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"List keys for a service account","description":"Return every key (active and revoked) issued under the service account. The plaintext is NEVER returned — only the 16-character display prefix. The Phase 9 admin UI uses this endpoint to render the keys-table under each account.","operationId":"list_service_account_keys_v1_service_accounts__account_id__keys_get","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Service Account Keys V1 Service Accounts  Account Id  Keys Get"}}}},"403":{"description":"Caller lacks Admin role (Member / API key / Service account)."},"404":{"description":"Service account not found in this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/service-accounts/{account_id}/keys/{key_id}":{"delete":{"tags":["Service Accounts (CLI Phase 8)"],"summary":"Revoke a single key","description":"Soft-delete one key under a service account. Sibling keys under the same account are unaffected. Returns 204 No Content on success; 403 if the caller lacks Admin role; 404 if either the account or key id is unknown / out of tenant.","operationId":"revoke_service_account_key_v1_service_accounts__account_id__keys__key_id__delete","parameters":[{"name":"account_id","in":"path","required":true,"schema":{"type":"string","title":"Account Id"}},{"name":"key_id","in":"path","required":true,"schema":{"type":"string","title":"Key Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/sso/discover":{"post":{"tags":["Enterprise SAML SSO","saml"],"summary":"Sso Discover","description":"Tell the sign-in form whether the email's domain is SAML-enabled\nAND whether a Clerk user already exists for that exact email.\n\nPublic endpoint — no auth. Always returns 200 with ``saml: false``\nfor unknown / unverified domains so this can't be used as a domain-\nenumeration oracle. Rate limiting is wired at the router level via\nslowapi (see ``main.py`` integration).\n\nSSO bypass: a member with ``sso_bypass=true`` for the matched tenant\ndeliberately gets ``saml: false`` so they can fall through to the\nClerk email/password sign-in.\n\nAccount-existence (``account_exists``) is an entry-screen UX hint —\nthe SsoEmailGate uses it to render Clerk's <SignIn /> for returning\nusers and <SignUp /> for first-time visitors, with the email\npre-filled either way. The lookup is soft-fail: any Clerk admin-API\nerror returns ``False`` so flaky Clerk never blocks a new user from\ncompleting sign-up. SAML domains skip the lookup because the IdP\nJIT flow handles both branches downstream.","operationId":"sso_discover_v1_auth_sso_discover_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DiscoverResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/saml/metadata":{"get":{"tags":["Enterprise SAML SSO","saml"],"summary":"Saml Metadata Endpoint","description":"Serve Quelvio's SP metadata XML — public, cacheable.","operationId":"saml_metadata_endpoint_v1_auth_saml_metadata_get","responses":{"200":{"description":"Successful Response"}}}},"/v1/auth/saml/login":{"get":{"tags":["Enterprise SAML SSO","saml"],"summary":"Saml Login","description":"SP-initiated entry — build AuthnRequest, store RelayState, redirect to IdP.","operationId":"saml_login_v1_auth_saml_login_get","parameters":[{"name":"domain","in":"query","required":true,"schema":{"type":"string","title":"Domain"}}],"responses":{"302":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/saml/acs":{"post":{"tags":["Enterprise SAML SSO","saml"],"summary":"Saml Acs","description":"ACS endpoint — both SP-initiated and IdP-initiated land here.","operationId":"saml_acs_v1_auth_saml_acs_post","responses":{"302":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/admin/saml/config":{"get":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Get Saml Config","operationId":"get_saml_config_v1_admin_saml_config_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlConfigResponse"}}}}}},"post":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Post Saml Config","description":"Upload IdP config. Either supply metadata XML or raw fields.","operationId":"post_saml_config_v1_admin_saml_config_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlConfigUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/saml/enable":{"post":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Post Saml Enable","operationId":"post_saml_enable_v1_admin_saml_enable_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlEnableRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SamlConfigResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/domain/verify-token":{"post":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Post Domain Verify Token","description":"(Re)generate the DNS TXT verification token. Resets dns_verified to false.","operationId":"post_domain_verify_token_v1_admin_domain_verify_token_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsVerifyTokenResponse"}}}}}}},"/v1/admin/domain/verify":{"post":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Post Domain Verify","description":"Trigger a DNS TXT lookup; flip ``dns_verified`` on success.","operationId":"post_domain_verify_v1_admin_domain_verify_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DnsVerifyAttemptResponse"}}}}}}},"/v1/admin/members/{member_id}/sso-bypass":{"patch":{"tags":["Enterprise SAML SSO · Admin","saml-admin"],"summary":"Patch Member Sso Bypass","description":"Toggle per-member SAML bypass. **Owner-only** — admin is rejected.","operationId":"patch_member_sso_bypass_v1_admin_members__member_id__sso_bypass_patch","parameters":[{"name":"member_id","in":"path","required":true,"schema":{"type":"string","title":"Member Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoBypassRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Patch Member Sso Bypass V1 Admin Members  Member Id  Sso Bypass Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scim/v2/ServiceProviderConfig":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"Get Service Provider Config","description":"RFC 7644 §5 service provider configuration.\n\nDocuments which optional features we support. v1 is intentionally\nspare: PATCH yes (the active=false deprovision flow), filter yes\n(simple eq on userName / externalId), everything else no.","operationId":"get_service_provider_config_scim_v2_ServiceProviderConfig_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/scim/v2/Schemas":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"List Schemas","operationId":"list_schemas_scim_v2_Schemas_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/scim/v2/Schemas/{schema_id}":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"Get Schema","operationId":"get_schema_scim_v2_Schemas__schema_id__get","parameters":[{"name":"schema_id","in":"path","required":true,"schema":{"type":"string","title":"Schema Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scim/v2/ResourceTypes":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"List Resource Types","operationId":"list_resource_types_scim_v2_ResourceTypes_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/scim/v2/ResourceTypes/{resource_type_id}":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"Get Resource Type","operationId":"get_resource_type_scim_v2_ResourceTypes__resource_type_id__get","parameters":[{"name":"resource_type_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Type Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scim/v2/Users":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"List Users","description":"Paginated user list with optional filter.","operationId":"list_users_scim_v2_Users_get","parameters":[{"name":"filter","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filter"}},{"name":"startIndex","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1,"title":"Startindex"}},{"name":"count","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":20,"title":"Count"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["Enterprise SCIM","scim"],"summary":"Create User","description":"Create a user. Idempotent on duplicate userName (200 not 409).","operationId":"create_user_scim_v2_Users_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/scim/v2/Users/{resource_id}":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"Get User","operationId":"get_user_scim_v2_Users__resource_id__get","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"tags":["Enterprise SCIM","scim"],"summary":"Replace User","operationId":"replace_user_scim_v2_Users__resource_id__put","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["Enterprise SCIM","scim"],"summary":"Patch User","operationId":"patch_user_scim_v2_Users__resource_id__patch","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Enterprise SCIM","scim"],"summary":"Delete User","description":"Soft-deprovision the user. RFC 7644 §3.6 — returns 204.","operationId":"delete_user_scim_v2_Users__resource_id__delete","parameters":[{"name":"resource_id","in":"path","required":true,"schema":{"type":"string","title":"Resource Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/scim/v2/Groups":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"List Groups","operationId":"list_groups_scim_v2_Groups_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["Enterprise SCIM","scim"],"summary":"Create Group","operationId":"create_group_scim_v2_Groups_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/scim/v2/Groups/{group_id}":{"get":{"tags":["Enterprise SCIM","scim"],"summary":"Get Group","operationId":"get_group_scim_v2_Groups__group_id__get","parameters":[{"name":"group_id","in":"path","required":true,"schema":{"type":"string","title":"Group Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/scim/tokens":{"get":{"tags":["Enterprise SCIM · Admin","scim-admin"],"summary":"List active SCIM tokens","description":"Return every non-revoked SCIM bearer token for the tenant. Plaintext is NEVER returned — only the 24-char display prefix. Use ``POST /v1/admin/scim/tokens`` to mint a new token (the plaintext is returned exactly once in that response).","operationId":"list_scim_tokens_v1_admin_scim_tokens_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Scim Tokens V1 Admin Scim Tokens Get"}}}},"403":{"description":"Caller lacks Admin role OR tenant plan does not grant SCIM."},"404":{"description":"Token not found in this tenant."}}},"post":{"tags":["Enterprise SCIM · Admin","scim-admin"],"summary":"Issue a new SCIM bearer token","description":"Mint a fresh ``qlv_scim_*`` bearer token for the tenant. The full plaintext is returned EXACTLY ONCE in the ``token`` field — save it on the spot, only the bcrypt hash is persisted. The IdP (Okta/Azure AD/etc.) uses this token as ``Authorization: Bearer qlv_scim_...`` on every SCIM call.","operationId":"issue_scim_token_v1_admin_scim_tokens_post","responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Issue Scim Token V1 Admin Scim Tokens Post"}}}},"403":{"description":"Caller lacks Admin role OR tenant plan does not grant SCIM."},"404":{"description":"Token not found in this tenant."},"409":{"description":"Token with this name already exists."}}}},"/v1/admin/scim/tokens/{token_id}":{"delete":{"tags":["Enterprise SCIM · Admin","scim-admin"],"summary":"Revoke a SCIM token","description":"Soft-revoke a token (sets ``revoked_at``). The SCIM bearer validator re-checks ``revoked_at IS NULL`` on every request, so revocation takes effect immediately on the next call from the IdP.","operationId":"revoke_scim_token_v1_admin_scim_tokens__token_id__delete","parameters":[{"name":"token_id","in":"path","required":true,"schema":{"type":"string","title":"Token Id"}}],"responses":{"204":{"description":"Successful Response"},"403":{"description":"Caller lacks Admin role OR tenant plan does not grant SCIM."},"404":{"description":"Token not found in this tenant."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/scim/users":{"get":{"tags":["Enterprise SCIM · Admin","scim-admin"],"summary":"List SCIM-provisioned users","description":"Return every active SCIM-provisioned user in the tenant (joined ``persona_mapping`` × ``tenant_members`` where the member is not deactivated). Used by the admin UI's 'Provisioned by IdP' view.","operationId":"list_scim_provisioned_users_v1_admin_scim_users_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response List Scim Provisioned Users V1 Admin Scim Users Get"}}}},"403":{"description":"Caller lacks Admin role OR tenant plan does not grant SCIM."},"404":{"description":"Token not found in this tenant."}}}},"/v1/enterprise/admin/usage/summary":{"get":{"tags":["Enterprise Admin · Usage"],"summary":"Get Usage Summary","description":"Tenant kT consumption summary over [from, to].\n\nReads ``token_consumption`` ONLY — no provider COGS surfaces here.\nTenant scope is taken from the authenticated session, never from\nthe request body or query string (CLAUDE.md anti-pattern).","operationId":"get_usage_summary_v1_enterprise_admin_usage_summary_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummaryResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/enterprise/admin/usage/queries":{"get":{"tags":["Enterprise Admin · Usage"],"summary":"List Usage Queries","description":"Tenant per-query kT breakdown over [from, to].\n\nLists retrieval-class ``token_consumption`` rows\n(retrieval/synthesis/long-output/multimodal addons) — Ingestion\nand Index daily live on their own line items in the summary\nendpoint and would clutter a \"queries\" view.\n\nReads ``token_consumption`` ONLY.","operationId":"list_usage_queries_v1_enterprise_admin_usage_queries_get","parameters":[{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"From"}},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"To"}},{"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/UsageQueriesResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/auth/sso-bridge":{"post":{"tags":["Auth (MCP SSO bridge)"],"summary":"Sso Bridge","description":"Bridge a Clerk/tenant-SSO JWT to a per-employee ephemeral API key.\n\nUsed by the MCP Worker's ``/oauth/callback`` after the user\nauthenticates with their identity provider. The Worker forwards the\nSSO JWT here and stores the returned ephemeral key (encrypted) in KV\nalongside ``member_id``/``email`` for later ``X-Employee-Id``\npropagation.\n\nAuth:\n  ``Authorization: Bearer <clerk_or_tenant_sso_jwt>``. Reuses\n  ``get_current_employee`` so this endpoint inherits every\n  invariant the dashboard auth path already enforces (suspension\n  gate, tenant resolution, member upsert).\n\nReturns 401 with ``code: no_tenant`` when the email's tenant does\nnot exist (W16 onboarding signal). Returns 403 if the resolved\nmember is suspended. Returns 200 + ephemeral key on success.\n\nThe key is shown ONCE in the response. The Worker MUST persist it\nencrypted; on token refresh the Worker is expected to mint a new\nephemeral key via this same flow rather than re-using the old one.","operationId":"sso_bridge_v1_auth_sso_bridge_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SsoBridgeResponse"}}}}}}},"/v1/auth/step-up":{"post":{"tags":["Auth (Step-up — Sec-H3)"],"summary":"Mint a short-lived step-up auth token (Sec-H3)","description":"Mint a short-lived step-up token bound to the caller's identity.\n\nThe 5-minute opaque token unlocks Sec-H3 sensitive operations on\nthe bulk PATCH endpoint. See the module docstring for the auth_method\nsemantics; the binding is verified at consumption time against\n``tenant_id`` + ``(member_id OR api_key_id)``.","operationId":"issue_step_up_token_v1_auth_step_up_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StepUpResponse"}}}},"422":{"description":"auth_method='mfa' was requested without an mfa_code."},"401":{"description":"Caller is not authenticated."}}}},"/oauth/device/authorize":{"post":{"tags":["OAuth (CLI device grant — cli-p5)"],"summary":"Start a device authorization grant","description":"RFC 8628 §3.1 — the CLI's first call. Returns a `device_code` (the CLI keeps + polls /oauth/token with it) and a `user_code` (the CLI shows to the human, who types it into `enterprise.quelvio.com/device`). Rate-limited at 10/min per IP. Public endpoint — no SSO, no API key. v1 accepts only `client_id=quelvio-cli` (first-party CLI). Unknown client_id returns 401 `invalid_client`.","operationId":"device_authorize_oauth_device_authorize_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationRequest"}}},"required":true},"responses":{"200":{"description":"Device handshake issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceAuthorizationResponse"},"example":{"device_code":"qlv_dvc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","user_code":"BCDF-GHJK","verification_uri":"https://enterprise.quelvio.com/device","verification_uri_complete":"https://enterprise.quelvio.com/device?code=BCDF-GHJK","expires_in":600,"interval":5}}}},"401":{"description":"Unknown client_id.","content":{"application/json":{"example":{"error":"invalid_client","error_description":"Unknown client_id. v1 accepts only 'quelvio-cli'."}}}},"429":{"description":"Rate-limit exceeded (10/min per IP)."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/token":{"post":{"tags":["OAuth (CLI device grant — cli-p5)"],"summary":"Exchange a device_code OR refresh_token for an access+refresh pair","description":"Accepts `application/x-www-form-urlencoded` per RFC 6749. Two supported grant types:\n\n- `urn:ietf:params:oauth:grant-type:device_code` — the CLI   redeems its device_code after the user approves via the   browser. Single-use; a second call with the same device_code   returns `invalid_grant`. Returns `authorization_pending`   while the user has not yet approved, `slow_down` when   polling faster than the advertised interval, `access_denied`   when the user clicked Deny, `expired_token` once 10 minutes   have passed.\n\n- `refresh_token` — rotates the pair. The submitted   refresh_token is revoked atomically; a fresh access +   refresh pair is returned. Reusing the old refresh after   rotation returns `invalid_grant`.","operationId":"token_endpoint_oauth_token_post","parameters":[{"name":"User-Agent","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User-Agent"}}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_token_endpoint_oauth_token_post"}}}},"responses":{"200":{"description":"Tokens issued.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TokenResponse"},"example":{"access_token":"qlv_oat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","refresh_token":"qlv_ort_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","token_type":"Bearer","expires_in":3600}}}},"400":{"description":"OAuth error: `authorization_pending`, `slow_down`, `expired_token`, `access_denied`, `invalid_grant`, or `unsupported_grant_type`.","content":{"application/json":{"example":{"error":"authorization_pending","error_description":"User has not yet approved the device."}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/revoke":{"post":{"tags":["OAuth (CLI device grant — cli-p5)"],"summary":"Revoke an access or refresh token","description":"RFC 7009 — always returns 200, even for unknown tokens. The spec explicitly forbids leaking whether the token was valid. Pass `token_type_hint=access_token` or `refresh_token` to skip one lookup (the hint is advisory; both columns are checked regardless). Public endpoint — the bearer of the token IS the only caller who can revoke it. On success the row's `revoked_at` is set; subsequent auth attempts with the token return 401.","operationId":"revoke_endpoint_oauth_revoke_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_revoke_endpoint_oauth_revoke_post"}}}},"responses":{"200":{"description":"Always — empty body, even on unknown tokens.","content":{"application/json":{"schema":{},"example":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/device/approve":{"post":{"tags":["OAuth (CLI device grant — cli-p5)"],"summary":"Approve a pending device authorization (frontend → backend)","description":"Called by the `enterprise.quelvio.com/device` page (Phase 6) after the signed-in employee clicks Approve. Authenticated via the user's SSO session (Clerk JWT or tenant IdP JWT). Flips `status='authorized'` on the matching `oauth_devices` row and binds the signed-in member as `user_id`. After this call the CLI's next poll of `/oauth/token` returns the access+refresh pair.","operationId":"device_approve_oauth_device_approve_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmRequest"}}},"required":true},"responses":{"200":{"description":"Device approved.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmResponse"},"example":{"ok":true}}}},"400":{"description":"OAuth error: `invalid_grant` (user_code unknown or not in `pending`), `expired_token` (TTL elapsed)."},"401":{"description":"Signed-in session required."},"403":{"description":"`access_denied` — the signed-in caller has no tenant_member binding, or is suspended."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/oauth/device/deny":{"post":{"tags":["OAuth (CLI device grant — cli-p5)"],"summary":"Deny a pending device authorization (frontend → backend)","description":"Called by the `enterprise.quelvio.com/device` page (Phase 6) after the signed-in employee clicks Deny. Flips `status='denied'` on the matching `oauth_devices` row. The CLI's next poll of `/oauth/token` returns `access_denied`.","operationId":"device_deny_oauth_device_deny_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmRequest"}}},"required":true},"responses":{"200":{"description":"Device denied.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceConfirmResponse"},"example":{"ok":true}}}},"400":{"description":"OAuth error: `invalid_grant`."},"401":{"description":"Signed-in session required."},"403":{"description":"`access_denied`."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/insights/dismiss":{"post":{"tags":["Insights"],"summary":"Dismiss Insight","description":"Dismiss a CTA for the authenticated member.\n\nTTL applied server-side per insight_type:\n  - upsell / dropoff        → 24h\n  - timeline_suggestion /\n    saved_search            → 14d","operationId":"dismiss_insight_v1_insights_dismiss_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissInsightRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissInsightResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/feedback":{"post":{"tags":["Feedback","Feedback"],"summary":"Submit feedback on a synthesis answer","description":"Record a thumbs-up / thumbs-down rating with optional structured reason.\n\nUPSERTs on ``(query_id, user_id)`` — re-submitting overwrites the\nprior row (latest wins). The full state-at-feedback-time snapshot\n(synthesis_model, embedding_model, retrieved + cited chunks,\nchunk_metadata_snapshot) is captured on every write because\nauthority recalcs nightly + reranker versions roll forward + the\nbackend never stores synthesis answer text — without day-1 capture\nthese signals cannot be reconstructed.","operationId":"submit_feedback_v1_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitFeedbackRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/internal/unsubscribe":{"post":{"tags":["Notifications · Internal","Notifications · Internal"],"summary":"RFC 8058 one-click unsubscribe receiver","operationId":"receive_unsubscribe_v1_internal_unsubscribe_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/internal/email-events":{"post":{"tags":["Notifications · Internal","Notifications · Internal"],"summary":"Receive Resend delivery / bounce / complaint webhook events","operationId":"receive_email_event_v1_internal_email_events_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":{"type":"string"},"type":"object","title":"Response Receive Email Event V1 Internal Email Events Post"}}}}}}},"/v1/internal/email-health":{"get":{"tags":["Notifications · Internal","Notifications · Internal"],"summary":"Operator config probe — does NOT send a real email","description":"Useful for post-deploy verification without paying for a send.\n\nReturns the provider name + configured From address + booleans for\nwhether the API key, webhook secret, and template manifest are set.\nSecret values are never echoed.","operationId":"email_health_v1_internal_email_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","title":"Response Email Health V1 Internal Email Health Get"}}}}}}},"/v1/referrals/my-code":{"get":{"tags":["Referrals","Referrals"],"summary":"Get My Code","operationId":"get_my_code_v1_referrals_my_code_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PersonalReferralCode"}}}}}}},"/v1/referrals/stats":{"get":{"tags":["Referrals","Referrals"],"summary":"Get Stats","operationId":"get_stats_v1_referrals_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralStats"}}}}}}},"/v1/referrals":{"get":{"tags":["Referrals","Referrals"],"summary":"List Referrals","operationId":"list_referrals_v1_referrals_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"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":[{"enum":["sent","clicked","signed_up","converted","rewarded","expired","revoked"],"type":"string"},{"type":"null"}],"title":"Status"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListReferralsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/referrals/email":{"post":{"tags":["Referrals","Referrals"],"summary":"Send Email Referral","operationId":"send_email_referral_v1_referrals_email_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendReferralRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralSummary"}}}},"400":{"description":"Eligibility failed OR recipient is on the suppression list","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EligibilityError"}}}},"429":{"description":"Rate limit exceeded (5/user/day)"},"502":{"description":"Email send failed downstream (SES error)"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/referrals/{referral_id}":{"delete":{"tags":["Referrals","Referrals"],"summary":"Delete Referral","operationId":"delete_referral_v1_referrals__referral_id__delete","parameters":[{"name":"referral_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Referral Id"}}],"requestBody":{"content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RevokeReferralRequest"},{"type":"null"}],"title":"Payload"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/referrals/redeem":{"post":{"tags":["Referrals","Referrals"],"summary":"Redeem Code","operationId":"redeem_code_v1_referrals_redeem_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemCodeRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/RedeemCodeSuccessResponse"},{"$ref":"#/components/schemas/RedeemCodeErrorResponse"}],"title":"Response Redeem Code V1 Referrals Redeem Post"}}}},"400":{"description":"Redeem failed — see error_code","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RedeemCodeErrorResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/referrals/celebrations":{"get":{"tags":["Referrals","Referrals"],"summary":"List Celebrations","description":"Pending celebration banners for the current authenticated user.\n\nAggregates across both sides — a user may receive at most one\n``referee_welcome`` (lifetime cap on being referred) plus one\n``referrer_conversion`` per referral they sent that converted.\nReturns an empty list when there is nothing to celebrate; never\n404.","operationId":"list_celebrations_v1_referrals_celebrations_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CelebrationsResponse"}}}}}}},"/v1/referrals/celebrations/{referral_id}/dismiss":{"post":{"tags":["Referrals","Referrals"],"summary":"Dismiss Celebration","description":"Mark the celebration banner dismissed for the caller's side.\n\nIdempotent: re-dismissing returns 204. Returns 404 when the\nreferral does not exist OR the caller is neither the referrer\nnor the referee on it (the route layer collapses both cases to\navoid leaking existence).","operationId":"dismiss_celebration_v1_referrals_celebrations__referral_id__dismiss_post","parameters":[{"name":"referral_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Referral Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/referrals/r/{code}":{"get":{"tags":["Referrals · Public","Referrals · Public"],"summary":"Public Referral Redirect","operationId":"public_referral_redirect_v1_referrals_r__code__get","parameters":[{"name":"code","in":"path","required":true,"schema":{"type":"string","minLength":1,"maxLength":16,"title":"Code"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/referrals":{"get":{"tags":["Admin · Referrals","Admin · Referrals"],"summary":"Admin List Referrals","operationId":"admin_list_referrals_v1_admin_referrals_get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"enum":["sent","clicked","signed_up","converted","rewarded","expired","revoked"],"type":"string"},{"type":"null"}],"title":"Status"}},{"name":"date_from","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date From"}},{"name":"date_to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Date To"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListReferralsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/referrals/{referral_id}":{"get":{"tags":["Admin · Referrals","Admin · Referrals"],"summary":"Admin Get Referral","operationId":"admin_get_referral_v1_admin_referrals__referral_id__get","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"referral_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Referral Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminReferralDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/referrals/{referral_id}/force-revoke":{"post":{"tags":["Admin · Referrals","Admin · Referrals"],"summary":"Admin Force Revoke","description":"Override the application-layer revoke gate. Permits revocation\nin any non-terminal state (e.g. for fraud-driven manual cleanup).","operationId":"admin_force_revoke_v1_admin_referrals__referral_id__force_revoke_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"referral_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Referral Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForceRevokeRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/referrals/{referral_id}/manually-mark-converted":{"post":{"tags":["Admin · Referrals","Admin · Referrals"],"summary":"Admin Manually Mark Converted","description":"Force-progress a stuck 'signed_up' referral to converted+rewarded.\n\nRe-uses ``ProcessOnboardingComplete`` so the same credit-grant +\nevent-publish flow runs as the normal cron path. Idempotent —\nre-runs against an already-rewarded row are no-ops.","operationId":"admin_manually_mark_converted_v1_admin_referrals__referral_id__manually_mark_converted_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"referral_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Referral Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManualConvertRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/admin/tenants/{tenant_id}/override-referral-cap":{"post":{"tags":["Admin · Referrals","Admin · Referrals"],"summary":"Admin Override Referral Cap","description":"Store a per-tenant cap in ``system_config``. The eligibility\nmodule reads the global key — a follow-up PR will teach it to\nprefer the per-tenant key when present. Without that follow-up\nthis endpoint still records the operator intent + auditable\nhistory, which is the v0.1 product requirement.","operationId":"admin_override_referral_cap_v1_admin_tenants__tenant_id__override_referral_cap_post","security":[{"APIKeyHeader":[]}],"parameters":[{"name":"tenant_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Tenant Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OverrideCapRequest"}}}},"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}}},"components":{"schemas":{"AbortUploadRequest":{"properties":{"upload_id":{"type":"string","maxLength":2048,"minLength":1,"title":"Upload Id"}},"type":"object","required":["upload_id"],"title":"AbortUploadRequest","description":"Request body for the abort endpoint."},"AbortUploadResponse":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["content_piece_id","status"],"title":"AbortUploadResponse","description":"Response body for the abort endpoint."},"AdminActionResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"message":{"type":"string","title":"Message"},"audit_log_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Audit Log Id"}},"type":"object","required":["success","message"],"title":"AdminActionResponse","description":"Generic response for admin write actions."},"AdminAggregateResponse":{"properties":{"header":{"$ref":"#/components/schemas/HeaderStatsResponse"},"daily_trend_30d":{"items":{"$ref":"#/components/schemas/DailyPointResponse"},"type":"array","title":"Daily Trend 30D"},"reason_distribution_7d":{"items":{"$ref":"#/components/schemas/ReasonCountResponse"},"type":"array","title":"Reason Distribution 7D"},"by_model_7d":{"items":{"$ref":"#/components/schemas/ModelBreakdownResponse"},"type":"array","title":"By Model 7D"},"by_reranker_7d":{"items":{"$ref":"#/components/schemas/RerankerBreakdownResponse"},"type":"array","title":"By Reranker 7D"}},"type":"object","required":["header","daily_trend_30d","reason_distribution_7d","by_model_7d","by_reranker_7d"],"title":"AdminAggregateResponse","description":"GET /v1/admin/feedback/aggregate response."},"AdminReferralDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"referrer_tenant_id":{"type":"string","format":"uuid","title":"Referrer Tenant Id"},"referrer_user_id":{"type":"string","format":"uuid","title":"Referrer User Id"},"referrer_email":{"type":"string","title":"Referrer Email"},"referee_email":{"type":"string","title":"Referee Email"},"referee_tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Referee Tenant Id"},"status":{"type":"string","enum":["sent","clicked","signed_up","converted","rewarded","expired","revoked"],"title":"Status"},"channel":{"type":"string","enum":["email","link"],"title":"Channel"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"signed_up_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Signed Up At"},"converted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Converted At"},"rewarded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rewarded At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"revoke_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoke Reason"},"gb_awarded_referrer":{"type":"integer","title":"Gb Awarded Referrer"},"gb_awarded_referee":{"type":"integer","title":"Gb Awarded Referee"},"reward_kt_referrer":{"type":"integer","title":"Reward Kt Referrer"},"reward_kt_referee":{"type":"integer","title":"Reward Kt Referee"},"referrer_credit_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Referrer Credit Id"},"referee_credit_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Referee Credit Id"},"metadata_":{"type":"object","title":"Metadata"}},"type":"object","required":["id","referrer_tenant_id","referrer_user_id","referrer_email","referee_email","referee_tenant_id","status","channel","created_at","expires_at","signed_up_at","converted_at","rewarded_at","revoked_at","revoke_reason","gb_awarded_referrer","gb_awarded_referee","reward_kt_referrer","reward_kt_referee","referrer_credit_id","referee_credit_id","metadata_"],"title":"AdminReferralDetail"},"AdminTenantListItem":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"plan_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan Tier"},"email_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Domain"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","status","created_at"],"title":"AdminTenantListItem","description":"One tenant row for the backoffice tenant-filter dropdown."},"AdminTenantListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AdminTenantListItem"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"AdminTenantListResponse"},"ApiKeySummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"key_prefix":{"type":"string","title":"Key Prefix"},"scope":{"type":"string","enum":["read","write"],"title":"Scope"},"created_by":{"type":"string","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"revoked_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoked By"}},"type":"object","required":["id","key_prefix","scope","created_by","created_at"],"title":"ApiKeySummary","description":"Sanitized API key — used by list endpoint and as base for mint\nresponse. Never includes the full plaintext key."},"ApproveNewFoldersRequest":{"properties":{"folder_ids":{"items":{"type":"string"},"type":"array","maxItems":500,"minItems":1,"title":"Folder Ids"}},"additionalProperties":false,"type":"object","required":["folder_ids"],"title":"ApproveNewFoldersRequest","description":"POST /v1/enterprise/connectors/{id}/approve-new-folders request body."},"ApproveNewFoldersResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"approved":{"items":{"type":"string"},"type":"array","title":"Approved"},"remaining_pending":{"type":"integer","title":"Remaining Pending"}},"type":"object","required":["connector_id","approved","remaining_pending"],"title":"ApproveNewFoldersResponse","description":"POST /v1/enterprise/connectors/{id}/approve-new-folders response."},"AssumptionOverride":{"properties":{"key":{"type":"string","title":"Key","description":"Namespaced slug, e.g. ``paying_tier_mix.team`` or ``gross_margin_target_pct``. Stable contract — the backoffice slider list reads from this directly."},"value":{"title":"Value","description":"Current value. JSON number for number/percent/currency_usd; nested object for value_type='object'. Percent values are in [0, 1] (NOT [0, 100])."},"value_type":{"type":"string","enum":["number","percent","currency_usd","object"],"title":"Value Type"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description","description":"Human-readable slider caption rendered in the backoffice."},"min_value":{"anyOf":[{},{"type":"null"}],"title":"Min Value","description":"Slider minimum. Null for value_type='object' (non-numeric assumptions don't render as a slider)."},"max_value":{"anyOf":[{},{"type":"null"}],"title":"Max Value","description":"Slider maximum."},"step":{"anyOf":[{},{"type":"null"}],"title":"Step","description":"Slider step granularity."},"updated_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated By","description":"Email of the last admin to edit this slider, or ``alembic-seed`` for rows that have never been touched."},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"UTC timestamp of the last write."}},"type":"object","required":["key","value","value_type","updated_at"],"title":"AssumptionOverride","description":"One slider's persisted state, as returned by the API."},"AssumptionOverridesResponse":{"properties":{"overrides":{"items":{"$ref":"#/components/schemas/AssumptionOverride"},"type":"array","title":"Overrides","description":"Every slider row, sorted by ``key``. Stable order so the backoffice slider list is deterministic."}},"type":"object","required":["overrides"],"title":"AssumptionOverridesResponse","description":"Top-level response for ``GET /v1/admin/cost-catalog/assumptions``."},"AuditEventResponse":{"properties":{"id":{"type":"integer","title":"Id"},"event_type":{"type":"string","title":"Event Type"},"actor_id":{"type":"string","title":"Actor Id"},"tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Id"},"employee_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Id"},"action":{"type":"string","title":"Action"},"resource_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Type"},"resource_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resource Id"},"metadata":{"type":"object","title":"Metadata"},"ip_address":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ip Address"},"client_kind":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Kind"},"client_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Label"},"client_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Version"},"command_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Command Name"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["id","event_type","actor_id","tenant_id","employee_id","action","resource_type","resource_id","metadata","ip_address","client_kind","client_label","client_version","command_name","created_at"],"title":"AuditEventResponse","description":"One audit row, in the response shape used by activity + admin pages."},"AuditListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AuditEventResponse"},"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":"AuditListResponse"},"AuditLogResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/app__domains__admin__presentation__tenants_billing_routes__AuditEntryResponse"},"type":"array","title":"Entries"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"ISO 8601 timestamp to pass back as ``before=`` for the next page. Null when no further entries exist."}},"type":"object","required":["entries"],"title":"AuditLogResponse","description":"Paginated tenant audit history, newest first."},"AuditResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/app__domains__memory__presentation__admin_routes__AuditEntryResponse"},"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":"AuditResponse"},"AutoRenewSettings":{"properties":{"fast":{"type":"boolean","title":"Fast"},"standard":{"type":"boolean","title":"Standard"},"deep":{"type":"boolean","title":"Deep"}},"type":"object","required":["fast","standard","deep"],"title":"AutoRenewSettings"},"AvailableRegion":{"properties":{"code":{"type":"string","title":"Code"},"display_name":{"type":"string","title":"Display Name"},"data_residency":{"type":"string","title":"Data Residency"},"latency_estimate_ms":{"type":"integer","title":"Latency Estimate Ms"}},"type":"object","required":["code","display_name","data_residency","latency_estimate_ms"],"title":"AvailableRegion"},"AvailableRegionsResponse":{"properties":{"regions":{"items":{"$ref":"#/components/schemas/AvailableRegion"},"type":"array","title":"Regions"}},"type":"object","required":["regions"],"title":"AvailableRegionsResponse"},"BackfillTenantSummaryItem":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"last_advanced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Advanced At"},"pieces_processed":{"type":"integer","title":"Pieces Processed"},"pieces_total":{"type":"integer","title":"Pieces Total"}},"type":"object","required":["tenant_id","name","status","pieces_processed","pieces_total"],"title":"BackfillTenantSummaryItem","description":"One row in the per-tenant strip embedded on the summary response.\n\nFrontend (backoffice PR #31) renders this beneath the headline\naggregate cards. ``status`` is the same closed enum as the aggregate\nbuckets: ``completed`` / ``errored`` / ``running`` / ``killed``."},"BackofficeOverrideDryRun":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"dry_run":{"type":"boolean","title":"Dry Run","default":true},"effect":{"type":"object","title":"Effect"}},"additionalProperties":false,"type":"object","required":["effect"],"title":"BackofficeOverrideDryRun","description":"Response shape for ``?dry_run=true`` calls (F-23 / OCI-5).\n\nReturns the computed effect without applying it. ``ok=true`` because\nthe dry-run computation succeeded; ``applied=false`` is the signal\nno state was mutated. No ``audit_log_id`` because no audit row is\nwritten for a dry-run."},"BackofficePlanChangeResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true},"status":{"type":"string","title":"Status"},"from_plan_tier":{"type":"string","title":"From Plan Tier"},"to_plan_tier":{"type":"string","title":"To Plan Tier"},"from_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"From Seat Count"},"to_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"To Seat Count"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"},"proration_applied_usd":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Proration Applied Usd"},"scheduled_change_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scheduled Change Id"},"flapping_active":{"type":"boolean","title":"Flapping Active","default":false},"plan_change_audit_log_id":{"type":"integer","title":"Plan Change Audit Log Id"},"backoffice_audit_log_id":{"type":"string","format":"uuid","title":"Backoffice Audit Log Id"},"applied_at":{"type":"string","format":"date-time","title":"Applied At"},"pack_refund_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Pack Refund Usd","default":"0.00"},"pack_refund_credit_ids":{"items":{"type":"string"},"type":"array","title":"Pack Refund Credit Ids"},"pool_reset":{"type":"boolean","title":"Pool Reset","default":false},"payg_closed":{"type":"boolean","title":"Payg Closed","default":false},"corpus_outcome":{"$ref":"#/components/schemas/BackofficePlanCorpusOutcome"}},"additionalProperties":false,"type":"object","required":["status","from_plan_tier","to_plan_tier","effective_at","plan_change_audit_log_id","backoffice_audit_log_id","applied_at"],"title":"BackofficePlanChangeResponse","description":"Response shape for the backoffice plan override apply path.\n\nMirrors WS-14's customer-side ``PlanChangeResponse`` verbatim plus\n``audit_log_id`` (which the customer shape already carries — the\nWS-14 plan_change_audit_log id) and ``backoffice_audit_log_id``\n(this endpoint's own audit row, distinct from the billing audit).\nFrontend dispatches the backoffice-audit row id to the audit-log\nreader on Screen 1."},"BackofficePlanCorpusOutcome":{"properties":{"new_corpus_tier_option_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"New Corpus Tier Option Id"},"new_total_gb":{"type":"integer","title":"New Total Gb","default":0},"new_additional_monthly_cost_usd":{"type":"string","title":"New Additional Monthly Cost Usd","default":"0.00"},"stripe_operation":{"type":"string","title":"Stripe Operation","default":"no_change"},"stripe_link_pending":{"type":"boolean","title":"Stripe Link Pending","default":false}},"additionalProperties":false,"type":"object","title":"BackofficePlanCorpusOutcome","description":"Corpus-side outcome of a plan override (subset of WS-14's shape)."},"BackofficePlanOverrideRequest":{"properties":{"target_plan_tier":{"type":"string","pattern":"^(free|team|growth|enterprise)$","title":"Target Plan Tier"},"target_seat_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Target Seat Count"},"target_corpus_tier_option_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Target Corpus Tier Option Id"},"reason":{"type":"string","maxLength":500,"minLength":10,"title":"Reason"}},"additionalProperties":false,"type":"object","required":["target_plan_tier","reason"],"title":"BackofficePlanOverrideRequest","description":"Body for ``POST /backoffice/tenant/{id}/plan/override``.\n\nMirrors the customer-facing ``PlanChangeRequest`` shape (WS-14\n``plan_transition_routes.py``) so the same handler invariants apply,\nminus ``self_serve`` constraints — the handler routes admin-source\ncalls past the self-serve gate and past the F-19 flap damper per\nthe OD-14-D / OD-14 backoffice carve-out.\n\n``target_seat_count`` may be omitted to keep the current seat count\n(the handler resolves missing fields per its docstring contract).\nFor pure-seat-only changes from the backoffice, the operator sets\n``target_plan_tier`` to the tenant's CURRENT tier and adjusts\nseats — same shape the customer ``seats/change`` endpoint translates\ninto internally.\n\nThe ``reason`` is required and width-bounded — it lands in the\n``backoffice_audit_log.payload_json`` for compliance review AND in\nthe WS-14 ``plan_change_audit_log.reason`` column for billing\noperations review (two audit trails: the backoffice one is \"who\npressed the button,\" the billing one is \"what changed\")."},"BackofficePlanTransitionPreviewResponse":{"properties":{"status":{"type":"string","title":"Status"},"from_plan_tier":{"type":"string","title":"From Plan Tier"},"to_plan_tier":{"type":"string","title":"To Plan Tier"},"from_seat_count":{"type":"integer","title":"From Seat Count"},"to_seat_count":{"type":"integer","title":"To Seat Count"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"},"proration_total_usd":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Proration Total Usd"},"subtotal_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Subtotal Usd"},"line_items":{"items":{"$ref":"#/components/schemas/BackofficePreviewLineItem"},"type":"array","title":"Line Items"},"estimated_pack_refund_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Estimated Pack Refund Usd","default":"0.00"},"estimated_pool_reset_kt":{"type":"integer","title":"Estimated Pool Reset Kt","default":0},"flapping_active":{"type":"boolean","title":"Flapping Active","default":false},"flap_prior_attempts":{"type":"integer","title":"Flap Prior Attempts","default":0},"existing_scheduled_change_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Existing Scheduled Change Id"},"existing_scheduled_target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Scheduled Target"},"blocker":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Blocker"},"corpus_warning":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Corpus Warning"}},"additionalProperties":false,"type":"object","required":["status","from_plan_tier","to_plan_tier","from_seat_count","to_seat_count","effective_at"],"title":"BackofficePlanTransitionPreviewResponse","description":"Response shape for the backoffice transition preview.\n\nReturns the WS-14 ``TransitionPreviewResponse`` shape verbatim\nEXCEPT that ``flapping_active`` and ``flap_prior_attempts`` are\nforced to ``false`` / ``0`` — backoffice-source calls bypass the\nF-19 flap damper at apply time, so the preview must not paint the\nflap-damped banner for the operator. The flap window is still\navailable via the WS-14 customer preview if the operator wants to\nsee what a self-serve attempt would look like; this endpoint\nintentionally hides it."},"BackofficePreviewLineItem":{"properties":{"description":{"type":"string","title":"Description"},"amount_usd":{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$","title":"Amount Usd"},"proration":{"type":"boolean","title":"Proration"},"quantity":{"type":"integer","title":"Quantity"},"price_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Id"}},"additionalProperties":false,"type":"object","required":["description","amount_usd","proration","quantity"],"title":"BackofficePreviewLineItem","description":"One line in the proration preview breakdown.\n\nShape matches WS-14's customer-side ``PreviewLineItem`` verbatim so\nthe backoffice frontend can render with the same component pattern\nT11's plan-change modal will use."},"BillEstimateLineItemResponse":{"properties":{"kind":{"type":"string","title":"Kind"},"description":{"type":"string","title":"Description"},"quantity":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quantity"},"unit_amount_usd":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit Amount Usd"},"amount_usd":{"type":"string","title":"Amount Usd"}},"type":"object","required":["kind","description","amount_usd"],"title":"BillEstimateLineItemResponse"},"BillEstimateResponse":{"properties":{"currency":{"type":"string","title":"Currency","default":"USD"},"line_items":{"items":{"$ref":"#/components/schemas/BillEstimateLineItemResponse"},"type":"array","title":"Line Items"},"subtotal_usd":{"type":"string","title":"Subtotal Usd"},"applied_credits_usd":{"type":"string","title":"Applied Credits Usd"},"estimated_total_usd":{"type":"string","title":"Estimated Total Usd"},"period_start":{"type":"string","title":"Period Start"},"period_end":{"type":"string","title":"Period End"}},"type":"object","required":["line_items","subtotal_usd","applied_credits_usd","estimated_total_usd","period_start","period_end"],"title":"BillEstimateResponse"},"BillingCapability":{"type":"string","enum":["sso_email","sso_google_workspace","sso_microsoft_entra","sso_saml","sso_custom_idp","scim_provisioning","query_fast","query_standard","query_deep","authority_scoring","ai_agent_integration","byok_encryption","at_cap_packs","payg_overage","corpus_self_serve_tiers","audit_log","service_accounts","offboarding"],"title":"BillingCapability","description":"v0.9 binary capability flags resolved from ``effective_tenant_plan``.\n\nString values match the Contract Lock ``BillingCapability`` enum\nexactly. Frontend, error contexts, and the reveal-mode response all\nkey off these strings — treat them as an API contract."},"BillingCapabilityState":{"properties":{"capability":{"$ref":"#/components/schemas/BillingCapability"},"enabled":{"type":"boolean","title":"Enabled"},"granted_on_tiers":{"items":{"type":"string"},"type":"array","title":"Granted On Tiers"}},"type":"object","required":["capability","enabled"],"title":"BillingCapabilityState","description":"One row in the resolved capability map.\n\n``enabled`` is the only field the backend consults for an access\ndecision. ``granted_on_tiers`` is an informational hint the\nfrontend uses to render the upgrade-CTA copy."},"BillingFeaturesResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"plan_tier":{"type":"string","title":"Plan Tier"},"region":{"type":"string","title":"Region"},"is_grandfathered":{"type":"boolean","title":"Is Grandfathered"},"grandfathered_until":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Grandfathered Until"},"capabilities":{"items":{"$ref":"#/components/schemas/BillingCapabilityState"},"type":"array","title":"Capabilities"}},"type":"object","required":["tenant_id","plan_tier","region","is_grandfathered","capabilities"],"title":"BillingFeaturesResponse","description":"Reveal-mode response shape (``GET /api/billing/features``)."},"BillingPlansResponseSchema":{"properties":{"region":{"type":"string","title":"Region"},"currency":{"type":"string","title":"Currency"},"billing_period":{"type":"string","enum":["monthly","annual"],"title":"Billing Period","description":"Current tenant subscription cadence."},"seat_price_multiplier":{"type":"number","title":"Seat Price Multiplier"},"plans":{"items":{"$ref":"#/components/schemas/PlanPricingSchema"},"type":"array","title":"Plans"},"comparison":{"items":{"$ref":"#/components/schemas/ComparisonCategorySchema"},"type":"array","title":"Comparison"}},"type":"object","required":["region","currency","billing_period","seat_price_multiplier","plans","comparison"],"title":"BillingPlansResponseSchema","description":"Response body — drives the entire ExplorePlansModal render."},"BlockerResponse":{"properties":{"type":{"type":"string","title":"Type"},"is_blocked":{"type":"boolean","title":"Is Blocked"},"reason":{"type":"string","title":"Reason","default":""},"details":{"type":"object","title":"Details"},"resolution_action":{"type":"object","title":"Resolution Action"},"fallback_action":{"type":"object","title":"Fallback Action"}},"type":"object","required":["type","is_blocked"],"title":"BlockerResponse","description":"One blocker's verdict — mirrors ``BlockerResult`` dataclass."},"Body_revoke_endpoint_oauth_revoke_post":{"properties":{"token":{"type":"string","title":"Token","default":""},"token_type_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Token Type Hint"}},"type":"object","title":"Body_revoke_endpoint_oauth_revoke_post"},"Body_token_endpoint_oauth_token_post":{"properties":{"grant_type":{"type":"string","title":"Grant Type","default":""},"device_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Device Code"},"refresh_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Refresh Token"},"client_id":{"type":"string","title":"Client Id","default":""}},"type":"object","title":"Body_token_endpoint_oauth_token_post"},"Body_upload_content_v1_enterprise_content_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"},"title":{"type":"string","title":"Title"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"author_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Email"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"permission_emails":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Permission Emails","description":"B+ Phase 3A. Optional JSON-encoded value: null (default — uploader-only), the literal string '\"__public_tenant__\"', or a JSON array of emails / '@domain' entries."}},"type":"object","required":["file","title"],"title":"Body_upload_content_v1_enterprise_content_post"},"Body_upload_content_v1_enterprise_content_upload_post":{"properties":{"file":{"type":"string","format":"binary","title":"File"}},"type":"object","required":["file"],"title":"Body_upload_content_v1_enterprise_content_upload_post"},"BoxAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"BoxAuthUrlResponse","description":"GET /v1/enterprise/connect/box response."},"BoxFolderItem":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"},"has_children":{"type":"boolean","title":"Has Children","default":true},"item_type":{"type":"string","enum":["folder","file"],"title":"Item Type","default":"folder"},"item_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Item Count"}},"type":"object","required":["id","name"],"title":"BoxFolderItem","description":"A single Box folder OR file returned by the picker.\n\n``has_children`` (v0.5.22 Phase 3 — CC-8) lets the FE adapter\ndisable the disclosure triangle for folders Box reports as empty,\nrather than showing a clickable triangle that fetches an empty\nlist. Backend computes from the Box API's\n``item_collection.total_count`` field; falls back to ``True`` when\nthe field is absent.\n\n``item_type`` + ``item_count`` (2026-05-26 fix) — the picker now\nreturns files alongside folders so users can see what's inside each\nfolder before confirming the sync. ``item_count`` is the count of\ndirect children of a folder (NOT recursive), surfaced from Box's\n``item_collection.total_count``; ``None`` for file items. The\nfrontend renders files as non-selectable read-only rows; only\nfolders carry checkboxes. Default ``item_type=\"folder\"`` preserves\nbackwards compatibility with adapter code that pre-dates the field."},"BoxFoldersResponse":{"properties":{"folders":{"items":{"$ref":"#/components/schemas/BoxFolderItem"},"type":"array","title":"Folders"},"selected_scope":{"items":{"type":"string"},"type":"array","title":"Selected Scope"}},"type":"object","required":["folders"],"title":"BoxFoldersResponse","description":"GET /v1/enterprise/connectors/{id}/box/folders response.\n\nLists the immediate children of the given Box folder (root folder id\nis ``\"0\"``). The caller walks the tree one level at a time, same UX\nas Google Drive's folder picker.\n``selected_scope`` echoes the list of folder ids the admin has\nalready persisted via /scope."},"BoxScopeRequest":{"properties":{"folder_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Folder Ids"}},"type":"object","title":"BoxScopeRequest","description":"POST /v1/enterprise/connectors/{id}/box/scope body.\n\nAdmin picks the set of Box folder ids to sync. Box has a single\nhierarchy so each entry is just a folder id — no sub-location\nconcept (contrast with SharePoint's {site_id, drive_ids})."},"BoxScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"folder_ids":{"items":{"type":"string"},"type":"array","title":"Folder Ids"},"folder_count":{"type":"integer","title":"Folder Count"},"subscriptions_created":{"type":"integer","title":"Subscriptions Created"},"subscriptions_removed":{"type":"integer","title":"Subscriptions Removed"}},"type":"object","required":["connector_id","folder_ids","folder_count","subscriptions_created","subscriptions_removed"],"title":"BoxScopeResponse","description":"POST /v1/enterprise/connectors/{id}/box/scope response."},"BulkPermissionsPatchBlockedPiece":{"properties":{"piece_id":{"type":"string","format":"uuid","title":"Piece Id","description":"ID of the piece whose mutation was refused."},"reason":{"type":"string","const":"permissions_managed_by_source","title":"Reason","description":"Machine-parsable rejection reason. Mirrors the per-piece PATCH endpoint's 409 ``error_code`` so frontend code can share one branch.","default":"permissions_managed_by_source"},"current_mode":{"type":"string","title":"Current Mode","description":"The piece's ``permission_resolution_mode`` at the time of the call — one of the connector-managed modes."}},"additionalProperties":false,"type":"object","required":["piece_id","current_mode"],"title":"BulkPermissionsPatchBlockedPiece","description":"One entry in the bulk PATCH ``blocked`` list (manage-access rule).\n\nPieces whose ``permission_resolution_mode`` is connector-managed\n(``per_file_acl`` / ``workspace_inherited`` / ``connector_default``\n/ ``explicit_public`` / ``unresolved_acl_skipped``) are filtered\nout of the mutating set and reported individually so the\ndashboard can render a \"N files skipped — managed at source\" toast\nwithout a second round-trip. Partial-success semantics: the bulk\nPATCH still mutates every editable piece in the matched set."},"BulkPermissionsPatchCapExceeded":{"properties":{"error_code":{"type":"string","const":"bulk_patch_cap_exceeded","title":"Error Code","default":"bulk_patch_cap_exceeded"},"error":{"type":"string","const":"bulk_patch_cap_exceeded","title":"Error","description":"DEPRECATED — mirrors ``error_code``. Slated for removal after the FS-contracts deprecation cycle closes.","default":"bulk_patch_cap_exceeded","deprecated":true},"affected_count":{"type":"integer","title":"Affected Count","description":"The actual count of matching pieces, for operator visibility."},"cap":{"type":"integer","title":"Cap","description":"The cap that was exceeded.","default":5000},"message":{"type":"string","title":"Message","default":"Bulk PATCH ``ids`` list is longer than the per-call cap. Narrow the ``ids`` slab and resubmit. (Filter-only-mode auto-paginates via ``next_page_token`` — see B-FE-14.)"}},"additionalProperties":false,"type":"object","required":["affected_count"],"title":"BulkPermissionsPatchCapExceeded","description":"``PATCH /v1/enterprise/sources/permissions`` 413 response.\n\nReturned when the resolved filter matches more pieces than the\nendpoint will mutate in a single call.\n\nFS-contracts: the wire body now carries ``error_code`` (canonical)\nin addition to the legacy ``error`` alias. Both fields point at\n``\"bulk_patch_cap_exceeded\"`` for the deprecation cycle. New\nconsumers MUST switch to ``error_code``."},"BulkPermissionsPatchDryRunResponse":{"properties":{"dry_run":{"type":"boolean","const":true,"title":"Dry Run","default":true},"affected_count":{"type":"integer","title":"Affected Count","description":"Number of pieces the real PATCH would update."},"sample_ids":{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":20,"title":"Sample Ids","description":"First 20 affected piece IDs (deterministic order). Cap matches the confirm dialog's preview affordance."},"cap_exceeded":{"type":"boolean","const":false,"title":"Cap Exceeded","default":false},"cap":{"type":"integer","title":"Cap","description":"The per-call cap. Echoed for frontend convenience.","default":5000}},"additionalProperties":false,"type":"object","required":["affected_count"],"title":"BulkPermissionsPatchDryRunResponse","description":"``PATCH /v1/enterprise/sources/permissions`` dry-run response.\n\nReturned when the request body sets ``dry_run=True`` and the\nfilter+cap checks pass. Same shape regardless of ``op`` — the dry\nrun reports what *would* be mutated, not the per-row diff. Sample\nIDs are capped at 20 (matches the dashboard's \"show me a few\nexamples\" affordance — operators drill into specific pieces via\n``GET /library/{piece_id}`` if they want to inspect more).\n\nCap-exceeded still 413s — the dry-run path runs the exact same\ncap gate as the mutating path so the frontend can rely on a single\nerror class either way."},"BulkPermissionsPatchFilter":{"properties":{"connector_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Connector Id","description":"EnterpriseConnector.id — affects all pieces synced from this connector."},"source":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Source","description":"Connector type literal (e.g. 'google_drive', 'notion'). Mapped to EnterpriseConnector.connector_type."},"format":{"anyOf":[{"type":"string","maxLength":20},{"type":"null"}],"title":"Format","description":"Content format literal (e.g. 'pdf', 'docx'). Matched against EnterpriseContentPiece.format."},"ids":{"anyOf":[{"items":{"type":"string","format":"uuid"},"type":"array","maxItems":5000},{"type":"null"}],"title":"Ids","description":"Explicit piece-id list. Combined with the other filters via AND."}},"additionalProperties":false,"type":"object","title":"BulkPermissionsPatchFilter","description":"Filter spec for ``PATCH /v1/enterprise/sources/permissions``.\n\nAT LEAST ONE of the four fields MUST be set (model-level validator).\nAn entirely blank filter is treated as a 422 ``filter_required``\n— refusing the call before it touches any data."},"BulkPermissionsPatchRateLimited":{"properties":{"error_code":{"type":"string","const":"bulk_patch_rate_limit_exceeded","title":"Error Code","default":"bulk_patch_rate_limit_exceeded"},"scope":{"type":"string","enum":["tenant","actor"],"title":"Scope","description":"Which sliding-window axis tripped — 'tenant' or 'actor'."},"retry_after_seconds":{"type":"integer","title":"Retry After Seconds","description":"Seconds until the oldest contributing record ages out of the 15-minute window. Mirrored in the Retry-After header."},"message":{"type":"string","title":"Message","default":"Bulk PATCH rate limit exceeded. The tenant or this actor has affected too many pieces in the last 15 minutes. Wait the retry_after_seconds interval and retry, or contact your tenant admin to widen the limits."}},"additionalProperties":false,"type":"object","required":["scope","retry_after_seconds"],"title":"BulkPermissionsPatchRateLimited","description":"``PATCH /v1/enterprise/sources/permissions`` 429 response (Sec-H3).\n\nReturned when the sliding-window rate limiter (per-tenant or per-actor)\nis exhausted. The ``Retry-After`` header carries the same integer."},"BulkPermissionsPatchRequest":{"properties":{"filter":{"$ref":"#/components/schemas/BulkPermissionsPatchFilter"},"op":{"type":"string","enum":["replace","add","remove"],"title":"Op"},"emails":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Emails","description":"Operator-supplied email list. Validated for sentinel injection up-front by the shared schema layer; the bulk service re-runs the guard with source='api'."},"dry_run":{"type":"boolean","title":"Dry Run","description":"When True, resolve + cap-check + count without mutating. Used by the frontend's confirm dialog (main-app PR #323) to surface \"this will affect N pieces\" before the operator commits.","default":false},"page_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Page Token","description":"B-FE-14: opaque cursor returned by the previous bulk-PATCH call as ``next_page_token``. Pass it back unchanged to resume filter-only-mode auto-pagination across a >cap matched set. Ignored on ``ids``-mode (the frontend auto-paginates explicit-IDs slabs client-side)."}},"additionalProperties":false,"type":"object","required":["filter","op","emails"],"title":"BulkPermissionsPatchRequest","description":"``PATCH /v1/enterprise/sources/permissions`` request body.\n\n``dry_run=True`` runs the same auth + filter resolution + cap check\nthat the real PATCH performs but does NOT mutate any pieces, write\naudit-log rows, persist a progress row, or schedule Qdrant\nfan-out. It returns :class:`BulkPermissionsPatchDryRunResponse`\nwith the affected count + a sample of the first 20 piece IDs so\nthe frontend can render a \"this will affect N pieces\" confirm\ndialog (main-app PR #323). Cap-exceeded still 413s.\n\nFS-contracts: this model inherits from\n:class:`PermissionEmailsListMixin` so the ``emails`` field shares\nthe same sentinel-injection guard + per-call cap as the Phase 3B\nper-piece PATCH. Membership validation still runs in the\napplication-layer ``validate_operator_emails_shape`` since the\nbulk path doesn't need per-entry membership lookups (operator is\ntrusted)."},"BulkPermissionsPatchResponse":{"properties":{"updated_count":{"type":"integer","title":"Updated Count","description":"Number of EnterpriseContentPiece rows that had permission_emails updated."},"audit_log_run_id":{"type":"string","format":"uuid","title":"Audit Log Run Id","description":"UUID identifying this run. Operator queries permission_backfill_progress.run_id to monitor Qdrant fan-out."},"next_page_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Page Token","description":"B-FE-14: opaque cursor when the filter matches more pieces than the per-call cap. NULL when the entire matched set was processed in this call. Frontend (FE-8, PR #327) keeps calling with this value in ``page_token`` until the response returns NULL."},"blocked":{"items":{"$ref":"#/components/schemas/BulkPermissionsPatchBlockedPiece"},"type":"array","title":"Blocked","description":"Manage-access rule: pieces in the filter set whose ``permission_resolution_mode`` is connector-managed (``per_file_acl`` / ``workspace_inherited`` / ``connector_default`` / ``explicit_public`` / ``unresolved_acl_skipped``) are skipped — Quelvio's PATCH surface reserves mutation for sources whose ACL Quelvio owns. The matching editable subset is still applied (partial-success semantics); this list lets the dashboard render a 'N files skipped — managed at source' summary without a second round-trip."}},"additionalProperties":false,"type":"object","required":["updated_count","audit_log_run_id"],"title":"BulkPermissionsPatchResponse","description":"``PATCH /v1/enterprise/sources/permissions`` 200 response."},"BulkPermissionsPatchStepUpRequired":{"properties":{"error_code":{"type":"string","const":"step_up_required","title":"Error Code","default":"step_up_required"},"step_up_endpoint":{"type":"string","title":"Step Up Endpoint","description":"Endpoint the operator should POST to in order to mint a step-up token. Pass the token via X-Step-Up-Token on the retried bulk PATCH call.","default":"/v1/auth/step-up"},"expanding_sentinels":{"items":{"type":"string"},"type":"array","title":"Expanding Sentinels","description":"Subset of __public_tenant__ / __public__ that this request would have introduced. Mirrored to the operator so they understand WHY step-up is required."},"message":{"type":"string","title":"Message","default":"This operation would expand visibility to a tenant-wide or public sentinel. A recent (≤5 min) step-up authentication is required. POST to the step_up_endpoint to mint a token, then retry with X-Step-Up-Token."}},"additionalProperties":false,"type":"object","required":["expanding_sentinels"],"title":"BulkPermissionsPatchStepUpRequired","description":"``PATCH /v1/enterprise/sources/permissions`` 403 response (Sec-H3).\n\nReturned when the request would expand visibility to a sentinel\n(``__public_tenant__`` / ``__public__``) via ``op='add'`` without a\nvalid step-up token."},"BulkRetryRequest":{"properties":{"content_ids":{"items":{"type":"string"},"type":"array","maxItems":100,"minItems":1,"title":"Content Ids"}},"type":"object","required":["content_ids"],"title":"BulkRetryRequest","description":"Request body for bulk enrichment retry."},"BulkUploadItem":{"properties":{"s3_uri":{"type":"string","maxLength":2000,"minLength":5,"title":"S3 Uri","description":"S3 URI: s3://bucket/key or just the key within the enterprise ingest bucket"},"title":{"type":"string","maxLength":500,"minLength":1,"title":"Title"},"object_size_bytes":{"type":"integer","minimum":1.0,"title":"Object Size Bytes","description":"Size of the S3 object in bytes — required for tier-gate enforcement (v1.3+)."},"author_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Name"},"author_email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Email"},"department":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Department"},"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails"}},"type":"object","required":["s3_uri","title","object_size_bytes"],"title":"BulkUploadItem","description":"A single item in a bulk upload request."},"BulkUploadItemResponse":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"title":{"type":"string","title":"Title"},"status":{"type":"string","title":"Status"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["content_piece_id","title","status"],"title":"BulkUploadItemResponse","description":"Status of a single item in a bulk upload."},"BulkUploadRequest":{"properties":{"items":{"items":{"$ref":"#/components/schemas/BulkUploadItem"},"type":"array","maxItems":100,"minItems":1,"title":"Items"}},"type":"object","required":["items"],"title":"BulkUploadRequest","description":"POST /v1/enterprise/content/bulk request body."},"BulkUploadResponse":{"properties":{"processed":{"type":"integer","title":"Processed"},"failed":{"type":"integer","title":"Failed"},"items":{"items":{"$ref":"#/components/schemas/BulkUploadItemResponse"},"type":"array","title":"Items"}},"type":"object","required":["processed","failed","items"],"title":"BulkUploadResponse","description":"POST /v1/enterprise/content/bulk response."},"BundleGrantRequest":{"properties":{"pack_type":{"type":"string","pattern":"^(fast|standard|deep)$","title":"Pack Type"},"quantity":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Quantity"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"},"override_plan_eligibility":{"type":"boolean","title":"Override Plan Eligibility","description":"Backoffice-only escape hatch for promo/marketing grants. When true, bypasses the available_on_plans catalog check so a pack type that is not normally offered on the tenant's current plan can still be granted (e.g. granting a Standard bundle to a Free tenant for a referral promo). The catalog row for (pack_type, region) must still exist — only the plan-tier scope check is bypassed. The grant is recorded with override_plan_eligibility=true in the structured audit log.","default":false}},"type":"object","required":["pack_type","quantity","reason"],"title":"BundleGrantRequest"},"BundleRevokeRequest":{"properties":{"bundle_id":{"type":"integer","minimum":1.0,"title":"Bundle Id"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"type":"object","required":["bundle_id","reason"],"title":"BundleRevokeRequest"},"BundleSchema":{"properties":{"id":{"type":"integer","title":"Id"},"pack_type":{"type":"string","title":"Pack Type"},"queries_purchased":{"type":"integer","title":"Queries Purchased"},"queries_remaining":{"type":"integer","title":"Queries Remaining"},"queries_consumed_pct":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","title":"Queries Consumed Pct"},"purchased_at":{"type":"string","format":"date-time","title":"Purchased At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"days_until_expiry":{"type":"integer","title":"Days Until Expiry"},"auto_renew_enabled":{"type":"boolean","title":"Auto Renew Enabled"},"source":{"type":"string","title":"Source"}},"type":"object","required":["id","pack_type","queries_purchased","queries_remaining","queries_consumed_pct","purchased_at","expires_at","days_until_expiry","auto_renew_enabled","source"],"title":"BundleSchema"},"CESMetricsResponse":{"properties":{"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"tasks":{"items":{"$ref":"#/components/schemas/CESTaskCardResponse"},"type":"array","title":"Tasks"},"overall_response_count":{"type":"integer","title":"Overall Response Count"},"overall_dismissal_count":{"type":"integer","title":"Overall Dismissal Count"}},"type":"object","required":["window_start","window_end","tasks","overall_response_count","overall_dismissal_count"],"title":"CESMetricsResponse"},"CESTaskBillingResponse":{"properties":{"task":{"type":"string","title":"Task"},"billing_state":{"type":"string","title":"Billing State"},"response_count":{"type":"integer","title":"Response Count"},"mean_score":{"type":"number","title":"Mean Score"}},"type":"object","required":["task","billing_state","response_count","mean_score"],"title":"CESTaskBillingResponse"},"CESTaskCardResponse":{"properties":{"task":{"type":"string","title":"Task"},"response_count":{"type":"integer","title":"Response Count"},"dismissal_count":{"type":"integer","title":"Dismissal Count"},"mean_score":{"type":"number","title":"Mean Score"},"response_rate":{"type":"number","title":"Response Rate"},"by_billing_state":{"items":{"$ref":"#/components/schemas/CESTaskBillingResponse"},"type":"array","title":"By Billing State"}},"type":"object","required":["task","response_count","dismissal_count","mean_score","response_rate","by_billing_state"],"title":"CESTaskCardResponse"},"CSATFeatureBillingResponse":{"properties":{"feature":{"type":"string","title":"Feature"},"billing_state":{"type":"string","title":"Billing State"},"response_count":{"type":"integer","title":"Response Count"},"mean_score":{"type":"number","title":"Mean Score"}},"type":"object","required":["feature","billing_state","response_count","mean_score"],"title":"CSATFeatureBillingResponse"},"CSATFeatureCardResponse":{"properties":{"feature":{"type":"string","title":"Feature"},"response_count":{"type":"integer","title":"Response Count"},"dismissal_count":{"type":"integer","title":"Dismissal Count"},"mean_score":{"type":"number","title":"Mean Score"},"response_rate":{"type":"number","title":"Response Rate"},"by_billing_state":{"items":{"$ref":"#/components/schemas/CSATFeatureBillingResponse"},"type":"array","title":"By Billing State"}},"type":"object","required":["feature","response_count","dismissal_count","mean_score","response_rate","by_billing_state"],"title":"CSATFeatureCardResponse"},"CSATMetricsResponse":{"properties":{"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"features":{"items":{"$ref":"#/components/schemas/CSATFeatureCardResponse"},"type":"array","title":"Features"},"overall_response_count":{"type":"integer","title":"Overall Response Count"},"overall_dismissal_count":{"type":"integer","title":"Overall Dismissal Count"}},"type":"object","required":["window_start","window_end","features","overall_response_count","overall_dismissal_count"],"title":"CSATMetricsResponse"},"CancelCheckResponse":{"properties":{"can_cancel":{"type":"boolean","title":"Can Cancel"},"tripped_count":{"type":"integer","title":"Tripped Count"},"blockers":{"items":{"$ref":"#/components/schemas/BlockerResponse"},"type":"array","title":"Blockers"}},"type":"object","required":["can_cancel","tripped_count","blockers"],"title":"CancelCheckResponse","description":"Full 9-blocker check result. Includes ALL blockers (not just\ntripped) so the operator sees STUB transparency alongside REAL\nverdicts."},"CancelRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":1000},{"type":"null"}],"title":"Reason"},"immediate":{"type":"boolean","title":"Immediate","default":false}},"type":"object","title":"CancelRequest","description":"Phase A spec §1907 — body is ``{reason?: string}``. WS-8 extends\nwith an optional ``immediate`` flag reserved for operator-side\naudit paths (always False at v0.9 launch — self-serve always\ncancels at period_end per F-20)."},"CancelResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"cancelled_at":{"type":"string","format":"date-time","title":"Cancelled At"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"},"immediate":{"type":"boolean","title":"Immediate"},"stripe_synced":{"type":"boolean","title":"Stripe Synced"},"scheduled_plan_change_cancelled":{"type":"boolean","title":"Scheduled Plan Change Cancelled"},"pack_refunds":{"items":{"$ref":"#/components/schemas/PackRefundSchema"},"type":"array","title":"Pack Refunds"},"total_pack_refund_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Total Pack Refund Usd","default":"0.00"},"idempotent_replay":{"type":"boolean","title":"Idempotent Replay","default":false}},"type":"object","required":["tenant_id","cancelled_at","effective_at","immediate","stripe_synced","scheduled_plan_change_cancelled"],"title":"CancelResponse"},"CancelScheduledChangeResponse":{"properties":{"voided":{"type":"boolean","title":"Voided"},"ws14_table_missing":{"type":"boolean","title":"Ws14 Table Missing"}},"type":"object","required":["voided","ws14_table_missing"],"title":"CancelScheduledChangeResponse","description":"Always 200. ``voided`` reflects whether a row was actually\ncancelled; ``ws14_table_missing`` is True when WS-14's table\nhasn't been migrated yet."},"CatalogResponse":{"properties":{"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"UTC timestamp the response was assembled. Always 'now'."},"catalog_commit_sha":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Catalog Commit Sha","description":"Git commit SHA of the running image, when available (read from ``GIT_SHA`` env var that the deploy workflow injects into the task definition). ``None`` in local dev."},"counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts","description":"Per-category entry counts. Stable shape: ``{'llm': N, 'infrastructure': N, 'third_party': N, 'plans': N}``."},"llm":{"items":{"$ref":"#/components/schemas/LLMEntryView"},"type":"array","title":"Llm"},"infrastructure":{"items":{"$ref":"#/components/schemas/InfrastructureEntryView"},"type":"array","title":"Infrastructure"},"third_party":{"items":{"$ref":"#/components/schemas/ThirdPartyEntryView"},"type":"array","title":"Third Party"},"plans":{"items":{"$ref":"#/components/schemas/PlanEntryView"},"type":"array","title":"Plans"}},"type":"object","required":["generated_at","counts","llm","infrastructure","third_party","plans"],"title":"CatalogResponse","description":"Top-level response for ``GET /v1/admin/cost-catalog``."},"CelebrationsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PendingCelebration"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"CelebrationsResponse","description":"Wrapper for ``GET /v1/referrals/celebrations``.\n\nEmpty result → ``{\"items\": []}``, NOT 404. ``items`` is capped at\n10 per call by the repository layer; older unseen celebrations\nremain queryable on subsequent calls after the user dismisses\nthe visible ones."},"CheckoutSessionRequest":{"properties":{"target_plan_tier":{"type":"string","enum":["team","growth"],"title":"Target Plan Tier","description":"The plan tier the tenant wants to subscribe to. Only subscribable tiers are accepted — Free has no subscription, and Enterprise is contract-priced (the UI opens a mailto: to sales for that case)."},"seat_count":{"type":"integer","maximum":99.0,"minimum":1.0,"title":"Seat Count","description":"Initial subscription quantity (number of seats). The frontend enforces an upper bound of 99 to match the seat stepper UI; the backend accepts the same range for defense in depth."},"billing_period":{"anyOf":[{"type":"string","enum":["monthly","annual"]},{"type":"null"}],"title":"Billing Period","description":"Optional override for the plan's default billing period. If omitted, the plan's ``billing_period`` column wins. The modal sends this when the customer toggles annual/monthly in the comparison view."},"corpus_tier_option_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Corpus Tier Option Id","description":"Optional ID of a corpus-tier add-on the customer picked in the modal's inline picker. When set, the checkout session includes the corpus tier as a second subscription line item alongside the seat plan. Default tier or NULL = no corpus add-on (the plan's default_corpus_gb still applies)."}},"type":"object","required":["target_plan_tier","seat_count"],"title":"CheckoutSessionRequest","description":"Request body for POST /api/billing/checkout-session."},"CheckoutSessionResponse":{"properties":{"url":{"type":"string","title":"Url","description":"Hosted Stripe Checkout URL. Single-use; expires after 24h per Stripe's default. The frontend should redirect immediately rather than caching it."}},"type":"object","required":["url"],"title":"CheckoutSessionResponse","description":"Response body — frontend redirects ``window.location.href = url``."},"ClassifierFeedbackEntryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"classifier_type":{"type":"string","title":"Classifier Type"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"original_classification":{"type":"string","title":"Original Classification"},"operator_classification":{"type":"string","title":"Operator Classification"},"operator_user_id":{"type":"string","format":"uuid","title":"Operator User Id"},"reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reasoning"},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"}},"type":"object","required":["id","tenant_id","classifier_type","source_id","original_classification","operator_classification","operator_user_id","reasoning","submitted_at"],"title":"ClassifierFeedbackEntryResponse"},"ClassifierFeedbackListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ClassifierFeedbackEntryResponse"},"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":"ClassifierFeedbackListResponse"},"ClassifierFeedbackSubmitRequest":{"properties":{"classifier_type":{"type":"string","enum":["tenant_memory_significance","user_event_significance"],"title":"Classifier Type"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"operator_classification":{"type":"string","maxLength":40,"minLength":1,"title":"Operator Classification"},"original_classification":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Original Classification"},"reasoning":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reasoning"}},"type":"object","required":["classifier_type","source_id","operator_classification"],"title":"ClassifierFeedbackSubmitRequest","description":"Operator override of a classifier verdict.\n\n``tenant_id`` is NOT accepted from the request body — it is derived\nserver-side from the source row keyed by (classifier_type, source_id).\nThis is a P0 invariant: an operator cannot submit feedback that\ncross-tenant-poisons the training corpus."},"ClassifierFeedbackSubmitResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"classifier_type":{"type":"string","title":"Classifier Type"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"original_classification":{"type":"string","title":"Original Classification"},"operator_classification":{"type":"string","title":"Operator Classification"},"submitted_at":{"type":"string","format":"date-time","title":"Submitted At"}},"type":"object","required":["id","tenant_id","classifier_type","source_id","original_classification","operator_classification","submitted_at"],"title":"ClassifierFeedbackSubmitResponse"},"ClearOverrideRequest":{"properties":{"confirmation_phrase":{"type":"string","title":"Confirmation Phrase","description":"Must equal `confirm_phrase_to_clear` from the GET listing."}},"type":"object","required":["confirmation_phrase"],"title":"ClearOverrideRequest","description":"DELETE /v1/admin/feature-flags/{key} body."},"ComparisonCategorySchema":{"properties":{"title":{"type":"string","title":"Title"},"rows":{"items":{"$ref":"#/components/schemas/ComparisonRowSchema"},"type":"array","title":"Rows"}},"type":"object","required":["title","rows"],"title":"ComparisonCategorySchema","description":"A heading + its rows in the comparison matrix."},"ComparisonRowSchema":{"properties":{"label":{"type":"string","title":"Label"},"values":{"additionalProperties":{"type":"string"},"type":"object","title":"Values","description":"Keyed by tier id ('free' | 'team' | 'growth' | 'enterprise'). Value is either a literal string for prose cells ('25 seats', '365 days') or the sentinels 'true' / 'false' for boolean cells so the frontend can render a checkmark/dash."}},"type":"object","required":["label","values"],"title":"ComparisonRowSchema","description":"One row in the feature comparison matrix."},"CompletePartInput":{"properties":{"part_number":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Part Number"},"etag":{"type":"string","maxLength":200,"minLength":1,"title":"Etag"}},"type":"object","required":["part_number","etag"],"title":"CompletePartInput","description":"A single ETag + part number reported by the frontend after PUT."},"CompleteUploadRequest":{"properties":{"content_piece_id":{"type":"string","maxLength":64,"minLength":1,"title":"Content Piece Id"},"upload_id":{"type":"string","maxLength":2048,"minLength":1,"title":"Upload Id"},"parts":{"items":{"$ref":"#/components/schemas/CompletePartInput"},"type":"array","maxItems":10000,"minItems":1,"title":"Parts"}},"type":"object","required":["content_piece_id","upload_id","parts"],"title":"CompleteUploadRequest","description":"Request body for the complete endpoint."},"CompleteUploadResponse":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"enterprise_piece_id":{"type":"string","title":"Enterprise Piece Id"},"status":{"type":"string","title":"Status"},"file_size_bytes":{"type":"integer","title":"File Size Bytes"}},"type":"object","required":["content_piece_id","enterprise_piece_id","status","file_size_bytes"],"title":"CompleteUploadResponse","description":"Response body for the complete endpoint."},"ConfirmUploadResponse":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["content_piece_id","status"],"title":"ConfirmUploadResponse","description":"POST /v1/enterprise/content/confirm/{content_piece_id} response."},"ConfluenceAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"ConfluenceAuthUrlResponse","description":"GET /v1/enterprise/connect/confluence response."},"ConfluenceFailedSubscription":{"properties":{"space_key":{"type":"string","title":"Space Key"},"error":{"type":"string","title":"Error"}},"type":"object","required":["space_key","error"],"title":"ConfluenceFailedSubscription","description":"One subscription create failure surfaced to the picker UI.\n\nv0.5.22 Phase 5 (CC-11). Pre-Phase-5 the route logged per-space\ncreate failures but the FE only saw an aggregate\n``subscriptions_created`` count — no way to tell the operator\nwhich specific space failed and why. This struct surfaces the\ndetail so the picker can render a per-space error chip."},"ConfluenceScopeRequest":{"properties":{"space_keys":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Space Keys"}},"type":"object","title":"ConfluenceScopeRequest","description":"POST /v1/enterprise/connectors/{id}/confluence/scope body.\n\nAdmin picks the set of Confluence space keys to sync.\n``key`` is the human-readable identifier (e.g. ``\"ENG\"``) that\nstays stable across renames at the API level."},"ConfluenceScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"space_keys":{"items":{"type":"string"},"type":"array","title":"Space Keys"},"space_count":{"type":"integer","title":"Space Count"},"subscriptions_created":{"type":"integer","title":"Subscriptions Created"},"subscriptions_removed":{"type":"integer","title":"Subscriptions Removed"},"failed_subscriptions":{"items":{"$ref":"#/components/schemas/ConfluenceFailedSubscription"},"type":"array","title":"Failed Subscriptions"}},"type":"object","required":["connector_id","space_keys","space_count","subscriptions_created","subscriptions_removed"],"title":"ConfluenceScopeResponse","description":"POST /v1/enterprise/connectors/{id}/confluence/scope response.\n\n``failed_subscriptions`` (v0.5.22 Phase 5 — CC-11) surfaces\nper-space subscription create failures so the picker UI can\nshow the operator exactly which spaces are NOT subscribed to\nwebhooks. The connector-level ``webhook_status`` column is also\nwritten (``'registration_failed'`` when this list is non-empty,\n``'active'`` when all desired subs were created, NULL when\nscope is empty)."},"ConfluenceSpaceItem":{"properties":{"id":{"type":"string","title":"Id"},"key":{"type":"string","title":"Key"},"name":{"type":"string","title":"Name"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"}},"type":"object","required":["id","key","name"],"title":"ConfluenceSpaceItem","description":"A single Confluence space returned by the picker."},"ConfluenceSpacesResponse":{"properties":{"spaces":{"items":{"$ref":"#/components/schemas/ConfluenceSpaceItem"},"type":"array","title":"Spaces"},"selected_scope":{"items":{"type":"string"},"type":"array","title":"Selected Scope"}},"type":"object","required":["spaces"],"title":"ConfluenceSpacesResponse","description":"GET /v1/enterprise/connectors/{id}/confluence/spaces response.\n\n``selected_scope`` echoes the list of space keys already\npersisted via /scope so the picker can render checkmarks."},"ConnectorImportIssuesResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"issues":{"items":{"$ref":"#/components/schemas/SyncFileIssue"},"type":"array","title":"Issues"},"total":{"type":"integer","title":"Total"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["connector_id","issues","total","limit","offset"],"title":"ConnectorImportIssuesResponse","description":"GET /v1/enterprise/connectors/{id}/import-issues response."},"ConnectorItem":{"properties":{"id":{"type":"string","title":"Id"},"type":{"type":"string","title":"Type"},"name":{"type":"string","title":"Name"},"status":{"type":"string","title":"Status"},"file_count":{"type":"integer","title":"File Count","default":0},"last_sync_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sync At"},"last_sync_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sync Status"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"primary_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Identifier"},"connected_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connected By Email"},"created_by_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Id"},"sync_status":{"type":"string","title":"Sync Status","default":"active"},"paused_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused At"},"external_id_set":{"type":"boolean","title":"External Id Set","description":"True iff the connector row has a non-NULL external_id. True for v0.6 Pattern A S3 connectors (the new wizard); false for legacy access-key S3 connectors. Frontend uses this to render the correct Sources card variant.","default":false},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region","description":"AWS region for S3 connectors (decrypted from config_encrypted). Null for non-S3 connector types or for S3 connectors where the region hasn't been auto-detected yet (pending_role_setup before sync)."},"kms_encrypted":{"type":"boolean","title":"Kms Encrypted","description":"True iff the S3 bucket uses SSE-KMS encryption (kms_key_arn is non-null in config_encrypted). The KMS key ARN value itself is NOT exposed — only this flag, which drives the lock icon in the Sources card.","default":false},"visible_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Visible Count","description":"Total items the integration can SEE in the source system (Notion pages shared with the integration, Slack channels the bot is in, etc.). NULL for connector_types whose poll handler has not yet been taught to write the field, and for rows pre-first-sync."},"visible_count_updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Visible Count Updated At","description":"Stamped each time visible_count is written. Lets the UI render an 'as of {date}' qualifier."}},"type":"object","required":["id","type","name","status"],"title":"ConnectorItem","description":"A connected data source."},"ConnectorListResponse":{"properties":{"connectors":{"items":{"$ref":"#/components/schemas/ConnectorItem"},"type":"array","title":"Connectors"}},"type":"object","required":["connectors"],"title":"ConnectorListResponse","description":"GET /v1/enterprise/connectors response."},"ConnectorSyncResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/ConnectorSyncRowResponse"},"type":"array","title":"Rows"}},"type":"object","required":["rows"],"title":"ConnectorSyncResponse"},"ConnectorSyncRowResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"connector_type":{"type":"string","title":"Connector Type"},"name":{"type":"string","title":"Name"},"primary_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Identifier"},"connected_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connected By Email"},"status":{"type":"string","title":"Status"},"sync_status":{"type":"string","title":"Sync Status"},"last_sync_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sync At"},"last_sync_age_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Last Sync Age Seconds"},"last_sync_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Sync Status"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"}},"type":"object","required":["connector_id","tenant_id","tenant_name","connector_type","name","status","sync_status"],"title":"ConnectorSyncRowResponse"},"ConsumptionResponse":{"properties":{"ingestion":{"$ref":"#/components/schemas/DimensionBreakdown"},"index":{"$ref":"#/components/schemas/DimensionBreakdown"},"retrieval":{"$ref":"#/components/schemas/DimensionBreakdown"},"total_tokens":{"type":"integer","title":"Total Tokens"},"total_cost_cents":{"type":"integer","title":"Total Cost Cents"},"period_start":{"type":"string","format":"date-time","title":"Period Start"},"period_end":{"type":"string","format":"date-time","title":"Period End"}},"type":"object","required":["ingestion","index","retrieval","total_tokens","total_cost_cents","period_start","period_end"],"title":"ConsumptionResponse","description":"GET /v1/enterprise/billing/consumption response.\n\nv0.8 dimension names (renamed from v0.5 ``created`` / ``maintained`` /\n``retrieved`` by alembic ``v107a001_rename_token_consumption_dimensions_v08``).\nSee CLAUDE.md §\"Billing (v0.8 Phase A)\"."},"ContentDetailResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"author_name":{"type":"string","title":"Author Name"},"author_email":{"type":"string","title":"Author Email"},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"},"content_hash":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Content Hash"},"format":{"type":"string","title":"Format"},"enrichment_stage":{"type":"string","title":"Enrichment Stage"},"enrichment_tier":{"type":"integer","title":"Enrichment Tier"},"enrichment_retry_count":{"type":"integer","title":"Enrichment Retry Count"},"enrichment_failed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Enrichment Failed At"},"quality_score":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Quality Score"},"authority_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Authority Score"},"taxonomy_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Domain"},"word_count":{"type":"integer","title":"Word Count"},"dedup_status":{"type":"string","title":"Dedup Status"},"chunk_count":{"type":"integer","title":"Chunk Count"},"queries_30d":{"type":"integer","title":"Queries 30D"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["id","title","author_name","author_email","format","enrichment_stage","enrichment_tier","enrichment_retry_count","word_count","dedup_status","chunk_count","queries_30d","created_at","updated_at"],"title":"ContentDetailResponse","description":"Full content piece detail for admin drill-down."},"ContentDrillDownResponse":{"properties":{"content_piece_id":{"type":"string","format":"uuid","title":"Content Piece Id"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"},"title":{"type":"string","title":"Title"},"enrichment_stage":{"type":"string","title":"Enrichment Stage"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"calls":{"items":{"$ref":"#/components/schemas/LlmCallResponse"},"type":"array","title":"Calls"},"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"total_input_units":{"type":"integer","title":"Total Input Units"},"total_output_units":{"type":"integer","title":"Total Output Units"}},"type":"object","required":["content_piece_id","tenant_id","tenant_name","title","enrichment_stage","created_at","calls","total_cost_usd_micros","total_input_units","total_output_units"],"title":"ContentDrillDownResponse"},"ContentListItemResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"},"title":{"type":"string","title":"Title"},"enrichment_stage":{"type":"string","title":"Enrichment Stage"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"cost_transcription_usd_micros":{"type":"integer","title":"Cost Transcription Usd Micros"},"cost_embedding_usd_micros":{"type":"integer","title":"Cost Embedding Usd Micros"},"cost_metadata_usd_micros":{"type":"integer","title":"Cost Metadata Usd Micros"},"cost_taxonomy_usd_micros":{"type":"integer","title":"Cost Taxonomy Usd Micros"},"cost_xref_usd_micros":{"type":"integer","title":"Cost Xref Usd Micros"},"llm_call_count":{"type":"integer","title":"Llm Call Count"},"rate_lookup_failed_count":{"type":"integer","title":"Rate Lookup Failed Count"}},"type":"object","required":["id","tenant_id","tenant_name","title","enrichment_stage","created_at","total_cost_usd_micros","cost_transcription_usd_micros","cost_embedding_usd_micros","cost_metadata_usd_micros","cost_taxonomy_usd_micros","cost_xref_usd_micros","llm_call_count","rate_lookup_failed_count"],"title":"ContentListItemResponse"},"ContentListResponse":{"properties":{"total":{"type":"integer","title":"Total","description":"Total matching rows (across pages)"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"items":{"items":{"$ref":"#/components/schemas/ContentListItemResponse"},"type":"array","title":"Items"},"window_totals":{"$ref":"#/components/schemas/ContentListWindowTotalsResponse"}},"type":"object","required":["total","limit","offset","items","window_totals"],"title":"ContentListResponse"},"ContentListWindowTotalsResponse":{"properties":{"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"cost_transcription_usd_micros":{"type":"integer","title":"Cost Transcription Usd Micros"},"cost_embedding_usd_micros":{"type":"integer","title":"Cost Embedding Usd Micros"},"cost_metadata_usd_micros":{"type":"integer","title":"Cost Metadata Usd Micros"},"cost_taxonomy_usd_micros":{"type":"integer","title":"Cost Taxonomy Usd Micros"},"cost_xref_usd_micros":{"type":"integer","title":"Cost Xref Usd Micros"},"llm_call_count":{"type":"integer","title":"Llm Call Count"},"piece_count":{"type":"integer","title":"Piece Count"},"rate_lookup_failed_count":{"type":"integer","title":"Rate Lookup Failed Count"}},"type":"object","required":["total_cost_usd_micros","cost_transcription_usd_micros","cost_embedding_usd_micros","cost_metadata_usd_micros","cost_taxonomy_usd_micros","cost_xref_usd_micros","llm_call_count","piece_count","rate_lookup_failed_count"],"title":"ContentListWindowTotalsResponse","description":"Sum across the entire filtered window (not just the current page)."},"ContentSearchItemResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"author_name":{"type":"string","title":"Author Name"},"format":{"type":"string","title":"Format"},"enrichment_stage":{"type":"string","title":"Enrichment Stage"},"authority_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Authority Score"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","title","author_name","format","enrichment_stage","created_at"],"title":"ContentSearchItemResponse","description":"Single content piece in search results."},"ContentSearchResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ContentSearchItemResponse"},"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":"ContentSearchResponse","description":"Paginated content search results."},"CorpusOptionSchema":{"properties":{"id":{"type":"integer","title":"Id"},"total_gb":{"type":"integer","title":"Total Gb"},"additional_monthly_cost_usd":{"type":"number","title":"Additional Monthly Cost Usd"},"is_default":{"type":"boolean","title":"Is Default"}},"type":"object","required":["id","total_gb","additional_monthly_cost_usd","is_default"],"title":"CorpusOptionSchema","description":"One corpus add-on tier (GB) available for a plan."},"CorpusTierChangeRequest":{"properties":{"corpus_tier_option_id":{"type":"integer","title":"Corpus Tier Option Id"}},"type":"object","required":["corpus_tier_option_id"],"title":"CorpusTierChangeRequest","description":"Per the Contract Lock (corrective #276): only ``corpus_tier_option_id``.\n\nEarlier drafts carried an ``effective_immediately`` flag whose\nsemantics were inconsistent with the events.md trigger enum (see\nWS-2 OCI-1, resolved upstream). The dominant contract is now\nexplicit: every self-serve downgrade is scheduled to the next\nperiod boundary; upgrades are immediate. No request flag."},"CorpusTierChangeResponse":{"properties":{"corpus_tier":{"$ref":"#/components/schemas/CorpusTierOptionSchema"},"proration_applied_usd":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Proration Applied Usd"},"effective_date":{"type":"string","format":"date-time","title":"Effective Date"},"next_invoice_estimate_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Next Invoice Estimate Usd"}},"type":"object","required":["corpus_tier","effective_date"],"title":"CorpusTierChangeResponse"},"CorpusTierOptionSchema":{"properties":{"id":{"type":"integer","title":"Id"},"total_gb":{"type":"integer","title":"Total Gb"},"additional_monthly_cost_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Additional Monthly Cost Usd"},"is_default":{"type":"boolean","title":"Is Default"}},"type":"object","required":["id","total_gb","additional_monthly_cost_usd","is_default"],"title":"CorpusTierOptionSchema"},"CorpusTierOptionsResponse":{"properties":{"current":{"$ref":"#/components/schemas/CorpusTierOptionSchema"},"available":{"items":{"$ref":"#/components/schemas/CorpusTierOptionSchema"},"type":"array","title":"Available"}},"type":"object","required":["current","available"],"title":"CorpusTierOptionsResponse"},"CorpusTierOverrideRequest":{"properties":{"corpus_tier_option_id":{"type":"integer","title":"Corpus Tier Option Id"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"additionalProperties":false,"type":"object","required":["corpus_tier_option_id","reason"],"title":"CorpusTierOverrideRequest"},"CorpusUsageResponse":{"properties":{"current_corpus_total_gb":{"type":"integer","title":"Current Corpus Total Gb"},"used_corpus_gb":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","title":"Used Corpus Gb"},"used_percent":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","title":"Used Percent"},"near_limit_threshold_percent":{"type":"integer","title":"Near Limit Threshold Percent","default":90},"chunks_indexed":{"type":"integer","title":"Chunks Indexed"}},"type":"object","required":["current_corpus_total_gb","used_corpus_gb","used_percent","chunks_indexed"],"title":"CorpusUsageResponse"},"CreateFilterPresetRequest":{"properties":{"name":{"type":"string","maxLength":80,"minLength":1,"title":"Name"},"mode":{"type":"string","enum":["include","exclude"],"title":"Mode"},"scope":{"type":"string","enum":["personal","tenant"],"title":"Scope","default":"personal"},"source_ids":{"items":{"type":"string"},"type":"array","title":"Source Ids"},"piece_ids":{"items":{"type":"string"},"type":"array","title":"Piece Ids"}},"type":"object","required":["name","mode"],"title":"CreateFilterPresetRequest","description":"POST /v1/enterprise/search/filter-presets body.\n\nAt least one of ``source_ids`` / ``piece_ids`` must be non-empty —\nenforced by the action, NOT by Pydantic, so the resulting error is\nthe same actionable PresetValidationError message regardless of\nwhether the caller sent ``[]`` for both or omitted them."},"CreateInviteRequest":{"properties":{"role_on_accept":{"type":"string","enum":["admin","member"],"title":"Role On Accept","default":"member"},"email_hint":{"anyOf":[{"type":"string","maxLength":320},{"type":"null"}],"title":"Email Hint"},"max_uses":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Max Uses"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"additionalProperties":false,"type":"object","title":"CreateInviteRequest"},"CreateInviteResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"role_on_accept":{"type":"string","enum":["admin","member"],"title":"Role On Accept"},"email_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Hint"},"max_uses":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Uses"},"used_count":{"type":"integer","title":"Used Count"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"rejected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rejected At"},"resend_count":{"type":"integer","title":"Resend Count","default":0},"token":{"type":"string","title":"Token"},"url":{"type":"string","title":"Url"},"status":{"type":"string","enum":["pending","accepted","rejected","expired","revoked"],"title":"Status","description":"Server-computed lifecycle status (see ``compute_invite_status``).","readOnly":true}},"type":"object","required":["id","role_on_accept","used_count","created_at","token","url","status"],"title":"CreateInviteResponse","description":"Creation response includes the raw token + invite URL (once only)."},"CreateKeyRequest":{"properties":{"expires_in_days":{"anyOf":[{"type":"integer","maximum":3650.0,"exclusiveMinimum":0.0},{"type":"null"}],"title":"Expires In Days","description":"Optional wall-clock expiry in days. Omit for a non-expiring key (default; matches the durable enterprise API key behavior)."}},"additionalProperties":false,"type":"object","title":"CreateKeyRequest"},"CreateKeyResponse":{"properties":{"id":{"type":"string","title":"Id"},"key_prefix":{"type":"string","title":"Key Prefix"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"key":{"type":"string","title":"Key"}},"additionalProperties":false,"type":"object","required":["id","key_prefix","expires_at","key"],"title":"CreateKeyResponse","description":"Response from POST /v1/service-accounts/{id}/keys.\n\n``key`` is the full plaintext — shown ONCE, never returned again.\nSave it on the spot. Store the bcrypt hash; we cannot recover the\nplaintext."},"CreateOrgRequest":{"properties":{"workspace_name":{"type":"string","maxLength":255,"minLength":2,"title":"Workspace Name"},"account_type":{"type":"string","enum":["personal","company"],"title":"Account Type","default":"company"},"region":{"type":"string","enum":["international","eu","usa"],"title":"Region","default":"international"},"company_size":{"anyOf":[{"type":"string","enum":["1-49","50-200","201-999","1000+"]},{"type":"null"}],"title":"Company Size"},"plan_tier":{"anyOf":[{"type":"string","enum":["free","team","growth","enterprise"]},{"type":"null"}],"title":"Plan Tier"},"seats":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":0.0},{"type":"null"}],"title":"Seats"},"vat_id":{"anyOf":[{"type":"string","maxLength":20,"minLength":4,"pattern":"^[A-Z]{2}[A-Z0-9]{2,18}$"},{"type":"null"}],"title":"Vat Id","description":"Optional EU VAT ID for B2B reverse-charge invoicing. Only accepted on EU-region signups."}},"additionalProperties":false,"type":"object","required":["workspace_name"],"title":"CreateOrgRequest","description":"POST /v1/enterprise/onboarding/create-org request body.\n\nv0.8.13 (PR 3 Account Type gate) added ``account_type``,\n``region``, ``company_size``. ``workspace_name`` stays required —\nPersonal users still get a workspace (it just defaults to the\nuser's display name on the frontend and is only ever shown to\nthem). Personal accounts are normalized by the action layer to\nregion=\"global\" + size=None, regardless of the API-region value the\nwrite-gate accepts.\n\nv0.9 Phase B WS-9:\n- ``region`` Literal is the **API write-gate**. It accepts exactly\n  the v0.9 three values (``international`` / ``eu`` / ``usa``)\n  that the public picker (``GET /api/regions/available``) offers.\n  The Contract Lock ``Region`` schema (``v09-openapi.yaml``)\n  carries six values because the **read vocabulary** —\n  ``effective_tenant_plan``, provider routing, billing multiplier\n  lookups — must resolve existing Phase A tenant rows that hold\n  legacy values (``global`` / ``europe`` / ``americas``). The\n  read vocabulary (six) and the write vocabulary (three) are\n  not the same thing; conflating them is contract drift. The\n  Lock stays as-is and the boundary is made explicit here.\n  The action-layer ``region`` parameter (in\n  ``create_org.py::CreateOrg.execute``) intentionally stays\n  permissive over the full six-value Lock vocabulary because the\n  personal-account branch force-writes ``\"global\"`` through the\n  same parameter and other internal callers may need the legacy\n  values; the narrowing is only at this Pydantic boundary.\n  Operator ruling, 2026-05-14 — see WS-9 cross-terminal notes\n  in ``docs/v09-phase-b/dispatch-log.md``.\n- ``plan_tier`` + ``seats`` are optional. When provided, the action\n  runs the signup-validation gate (F-15 + F-16): Enterprise\n  (``is_self_serve = false``) is rejected with 403 \"Contact Sales\";\n  a per-seat-priced plan with ``seats < 1`` is rejected with 422.\n  When omitted, the existing trial-on-Free behavior is preserved\n  and no plan_tier is recorded on the tenant row."},"CreateOrgResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"email_domain":{"type":"string","title":"Email Domain"},"member":{"$ref":"#/components/schemas/MembershipResponse"}},"type":"object","required":["tenant_id","tenant_name","email_domain","member"],"title":"CreateOrgResponse","description":"Tenant + first-member returned after self-service org creation."},"CreateServiceAccountRequest":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Description"},"scopes":{"items":{"$ref":"#/components/schemas/ScopeEntry"},"type":"array","title":"Scopes","description":"Admin-defined corpus scope. Empty list means no access — this is deny-by-default for safety. Add at least one scope entry before issuing a key."}},"additionalProperties":false,"type":"object","required":["name"],"title":"CreateServiceAccountRequest"},"CreateTenantRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name"},"email_domain":{"type":"string","maxLength":255,"minLength":3,"title":"Email Domain"},"sso_provider":{"type":"string","title":"Sso Provider","description":"okta | azure_ad | google_workspace"},"sso_config":{"type":"object","title":"Sso Config","description":"JWKS URI, issuer, client_id, client_secret_arn"}},"type":"object","required":["name","email_domain","sso_provider"],"title":"CreateTenantRequest","description":"POST /v1/admin/tenants request body."},"CreateTenantResponse":{"properties":{"tenant":{"$ref":"#/components/schemas/TenantResponse"},"api_key":{"type":"string","title":"Api Key","description":"Shown once. Store securely."}},"type":"object","required":["tenant","api_key"],"title":"CreateTenantResponse","description":"POST /v1/admin/tenants response — includes one-time API key."},"CreateThreadRequest":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Title"}},"type":"object","title":"CreateThreadRequest","description":"POST /v1/enterprise/threads request body.\n\n``title`` is optional — defaults to 'Untitled' when omitted. The\ntypical creation path mints threads lazily on the first streaming\nquery (handled in the retrieval routes), so this endpoint is a\nrare-path explicit-create."},"CreateWebhookRequest":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name"},"provider":{"type":"string","enum":["workday","bamboohr","rippling","custom"],"title":"Provider"},"secret":{"anyOf":[{"type":"string","maxLength":512,"minLength":16},{"type":"null"}],"title":"Secret"}},"type":"object","required":["name","provider"],"title":"CreateWebhookRequest"},"CustomerQueryRowResponse":{"properties":{"consumption_id":{"type":"string","format":"uuid","title":"Consumption Id"},"operation_type":{"type":"string","title":"Operation Type"},"region":{"type":"string","title":"Region"},"computed_kt":{"type":"integer","title":"Computed Kt"},"raw_count":{"type":"integer","title":"Raw Count"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"source_event_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Event Id"},"content_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Content Id"}},"type":"object","required":["consumption_id","operation_type","region","computed_kt","raw_count","occurred_at"],"title":"CustomerQueryRowResponse","description":"One row in the customer per-query kT list."},"DailyCapsResponse":{"properties":{"aggregation_level":{"type":"string","title":"Aggregation Level"},"pool_total_today_kt":{"type":"integer","title":"Pool Total Today Kt"},"pool_consumed_today_kt":{"type":"integer","title":"Pool Consumed Today Kt"},"pool_remaining_kt":{"type":"integer","title":"Pool Remaining Kt"},"pool_resets_at":{"type":"string","title":"Pool Resets At"},"query_kt_cost":{"$ref":"#/components/schemas/_QueryKtCostShape"},"equivalent_remaining":{"$ref":"#/components/schemas/_EquivalentRemainingShape"},"query_counts_today":{"$ref":"#/components/schemas/_QueryCountsTodayShape"},"scope":{"type":"string","title":"Scope","default":"tenant"}},"type":"object","required":["aggregation_level","pool_total_today_kt","pool_consumed_today_kt","pool_remaining_kt","pool_resets_at","query_kt_cost","equivalent_remaining","query_counts_today"],"title":"DailyCapsResponse"},"DailyMetricResponse":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"query_count":{"type":"integer","title":"Query Count"},"input_tokens":{"type":"integer","title":"Input Tokens"},"output_tokens":{"type":"integer","title":"Output Tokens"},"provider_cost_usd_micros":{"type":"integer","title":"Provider Cost Usd Micros"}},"type":"object","required":["day","query_count","input_tokens","output_tokens","provider_cost_usd_micros"],"title":"DailyMetricResponse"},"DailyPointResponse":{"properties":{"day":{"type":"string","format":"date-time","title":"Day"},"thumbs_up":{"type":"integer","title":"Thumbs Up"},"thumbs_down":{"type":"integer","title":"Thumbs Down"}},"type":"object","required":["day","thumbs_up","thumbs_down"],"title":"DailyPointResponse"},"DashboardMetricsResponse":{"properties":{"total_tenants":{"type":"integer","title":"Total Tenants"},"total_employees":{"type":"integer","title":"Total Employees"},"total_content_pieces":{"type":"integer","title":"Total Content Pieces"},"live_content_pieces":{"type":"integer","title":"Live Content Pieces"},"queries_today":{"type":"integer","title":"Queries Today"},"queries_30d":{"type":"integer","title":"Queries 30D"}},"type":"object","required":["total_tenants","total_employees","total_content_pieces","live_content_pieces","queries_today","queries_30d"],"title":"DashboardMetricsResponse","description":"Cross-product snapshot for the overview page."},"DashboardResponse":{"properties":{"period_start":{"type":"string","format":"date","title":"Period Start"},"period_end":{"type":"string","format":"date","title":"Period End"},"total_queries":{"type":"integer","title":"Total Queries"},"total_input_tokens":{"type":"integer","title":"Total Input Tokens"},"total_output_tokens":{"type":"integer","title":"Total Output Tokens"},"provider_cost_usd_micros":{"type":"integer","title":"Provider Cost Usd Micros"},"customer_kt_total":{"type":"integer","title":"Customer Kt Total"},"active_tenant_count":{"type":"integer","title":"Active Tenant Count"},"daily":{"items":{"$ref":"#/components/schemas/DailyMetricResponse"},"type":"array","title":"Daily"},"top_tenants_by_cost":{"items":{"$ref":"#/components/schemas/TopEntityResponse"},"type":"array","title":"Top Tenants By Cost"},"top_models_by_cost":{"items":{"$ref":"#/components/schemas/TopEntityResponse"},"type":"array","title":"Top Models By Cost"}},"type":"object","required":["period_start","period_end","total_queries","total_input_tokens","total_output_tokens","provider_cost_usd_micros","customer_kt_total","active_tenant_count","daily","top_tenants_by_cost","top_models_by_cost"],"title":"DashboardResponse"},"DepartureFlagRequest":{"properties":{"person_email":{"type":"string","maxLength":255,"minLength":1,"title":"Person Email"},"person_name":{"type":"string","maxLength":255,"minLength":1,"title":"Person Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"departure_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Departure Date"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"}},"type":"object","required":["person_email","person_name"],"title":"DepartureFlagRequest","description":"POST /v1/enterprise/intelligence/departure request body."},"DepartureFlagResponse":{"properties":{"id":{"type":"string","title":"Id"},"person_email":{"type":"string","title":"Person Email"},"person_name":{"type":"string","title":"Person Name"},"status":{"type":"string","title":"Status"},"departure_date":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Departure Date"},"transfer_plans":{"items":{"$ref":"#/components/schemas/TransferPlanSchema"},"type":"array","title":"Transfer Plans"}},"type":"object","required":["id","person_email","person_name","status"],"title":"DepartureFlagResponse","description":"POST /v1/enterprise/intelligence/departure response."},"DeviceAuthorizationRequest":{"properties":{"client_id":{"type":"string","maxLength":64,"title":"Client Id"}},"type":"object","required":["client_id"],"title":"DeviceAuthorizationRequest"},"DeviceAuthorizationResponse":{"properties":{"device_code":{"type":"string","title":"Device Code"},"user_code":{"type":"string","title":"User Code"},"verification_uri":{"type":"string","title":"Verification Uri"},"verification_uri_complete":{"type":"string","title":"Verification Uri Complete"},"expires_in":{"type":"integer","title":"Expires In"},"interval":{"type":"integer","title":"Interval"}},"type":"object","required":["device_code","user_code","verification_uri","verification_uri_complete","expires_in","interval"],"title":"DeviceAuthorizationResponse"},"DeviceConfirmRequest":{"properties":{"user_code":{"type":"string","maxLength":16,"title":"User Code"}},"type":"object","required":["user_code"],"title":"DeviceConfirmRequest"},"DeviceConfirmResponse":{"properties":{"ok":{"type":"boolean","title":"Ok","default":true}},"type":"object","title":"DeviceConfirmResponse"},"DimensionBreakdown":{"properties":{"tokens":{"type":"integer","title":"Tokens"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["tokens","count"],"title":"DimensionBreakdown","description":"Token consumption breakdown for a single dimension."},"DisconnectPreviewResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"connector_type":{"type":"string","title":"Connector Type"},"primary_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Identifier"},"pieces_count":{"type":"integer","title":"Pieces Count"},"files_count":{"type":"integer","title":"Files Count"}},"type":"object","required":["connector_id","connector_type","primary_identifier","pieces_count","files_count"],"title":"DisconnectPreviewResponse","description":"GET /v1/enterprise/sources/{id}/disconnect-preview response.\n\nPowers the two-step confirmation modal on the Sources page (Track I\nI1): step 1 shows the counts + identifier, step 2 requires the user\nto type the exact primary_identifier to enable the destructive\naction."},"DiscoverRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"}},"type":"object","required":["email"],"title":"DiscoverRequest"},"DiscoverResponse":{"properties":{"saml":{"type":"boolean","title":"Saml"},"login_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Login Url"},"account_exists":{"type":"boolean","title":"Account Exists","default":false}},"type":"object","required":["saml"],"title":"DiscoverResponse"},"DismissInsightRequest":{"properties":{"insight_type":{"type":"string","maxLength":40,"minLength":1,"title":"Insight Type","description":"One of: upsell | dropoff | timeline_suggestion | saved_search"}},"type":"object","required":["insight_type"],"title":"DismissInsightRequest","description":"POST /v1/insights/dismiss body."},"DismissInsightResponse":{"properties":{"id":{"type":"string","title":"Id"},"insight_type":{"type":"string","title":"Insight Type"},"dismissed_at":{"type":"string","title":"Dismissed At"},"expires_at":{"type":"string","title":"Expires At"}},"type":"object","required":["id","insight_type","dismissed_at","expires_at"],"title":"DismissInsightResponse","description":"POST /v1/insights/dismiss response."},"DismissNewFoldersRequest":{"properties":{"folder_ids":{"items":{"type":"string"},"type":"array","maxItems":500,"minItems":1,"title":"Folder Ids"}},"additionalProperties":false,"type":"object","required":["folder_ids"],"title":"DismissNewFoldersRequest","description":"POST /v1/enterprise/connectors/{id}/dismiss-new-folders request body."},"DismissNewFoldersResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"dismissed":{"items":{"type":"string"},"type":"array","title":"Dismissed"},"remaining_pending":{"type":"integer","title":"Remaining Pending"}},"type":"object","required":["connector_id","dismissed","remaining_pending"],"title":"DismissNewFoldersResponse","description":"POST /v1/enterprise/connectors/{id}/dismiss-new-folders response."},"DismissSurveyRequest":{"properties":{"trigger_event":{"type":"string","maxLength":50,"title":"Trigger Event"},"trigger_context":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Trigger Context"},"idempotency_key":{"type":"string","format":"uuid","title":"Idempotency Key"}},"type":"object","required":["trigger_event","idempotency_key"],"title":"DismissSurveyRequest"},"DismissSurveyResponse":{"properties":{"recorded":{"type":"boolean","title":"Recorded"},"record_id":{"type":"string","format":"uuid","title":"Record Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"duplicate":{"type":"boolean","title":"Duplicate","default":false}},"type":"object","required":["recorded","record_id","created_at"],"title":"DismissSurveyResponse"},"DnsVerifyAttemptResponse":{"properties":{"dns_verified":{"type":"boolean","title":"Dns Verified"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["dns_verified","reason"],"title":"DnsVerifyAttemptResponse"},"DnsVerifyTokenResponse":{"properties":{"domain":{"type":"string","title":"Domain"},"txt_host":{"type":"string","title":"Txt Host"},"txt_value":{"type":"string","title":"Txt Value"},"dns_verified":{"type":"boolean","title":"Dns Verified"}},"type":"object","required":["domain","txt_host","txt_value","dns_verified"],"title":"DnsVerifyTokenResponse"},"DomainCoverageSchema":{"properties":{"taxonomy_domain":{"type":"string","title":"Taxonomy Domain"},"document_count":{"type":"integer","title":"Document Count"},"chunk_count":{"type":"integer","title":"Chunk Count"},"expert_count":{"type":"integer","title":"Expert Count"},"coverage_level":{"type":"string","title":"Coverage Level"},"single_expert":{"type":"boolean","title":"Single Expert"},"top_expert":{"anyOf":[{"$ref":"#/components/schemas/ExpertSummarySchema"},{"type":"null"}]},"consistency_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Consistency Score"},"unanswered_query_count":{"type":"integer","title":"Unanswered Query Count","default":0},"last_content_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Content At"}},"type":"object","required":["taxonomy_domain","document_count","chunk_count","expert_count","coverage_level","single_expert"],"title":"DomainCoverageSchema","description":"Per-domain coverage info for the knowledge map treemap."},"DomainDetailResponse":{"properties":{"taxonomy_domain":{"type":"string","title":"Taxonomy Domain"},"document_count":{"type":"integer","title":"Document Count"},"chunk_count":{"type":"integer","title":"Chunk Count"},"experts":{"items":{"$ref":"#/components/schemas/ExpertSummarySchema"},"type":"array","title":"Experts"},"documents":{"items":{"$ref":"#/components/schemas/DomainDocumentSchema"},"type":"array","title":"Documents"},"unanswered_queries":{"items":{"$ref":"#/components/schemas/UnansweredQuerySchema"},"type":"array","title":"Unanswered Queries"},"coverage_level":{"type":"string","title":"Coverage Level"},"last_content_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Content At"}},"type":"object","required":["taxonomy_domain","document_count","chunk_count","experts","documents","unanswered_queries","coverage_level"],"title":"DomainDetailResponse","description":"GET /v1/enterprise/knowledge-map/{domain} response.\n\n``unanswered_queries`` changed shape in this PR: previously a flat\n``list[str]`` of opaque query hashes, now a list of structured\nsummaries with timestamps so the drill-down panel can render\nactionable rows (\"asked 3 days ago\"). Hashes are NOT displayed in\nthe UI — the field is here only as a stable row key for React."},"DomainDocumentSchema":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"source_connector":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Connector"},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"},"last_synced_at":{"type":"string","format":"date-time","title":"Last Synced At"}},"type":"object","required":["id","title","last_synced_at"],"title":"DomainDocumentSchema","description":"One indexed document for the Knowledge Map drill-down panel."},"DomainsListResponse":{"properties":{"domains":{"items":{"$ref":"#/components/schemas/DomainCoverageSchema"},"type":"array","title":"Domains"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["domains","total"],"title":"DomainsListResponse","description":"GET /v1/enterprise/domains response.\n\nPowers the MCP `list_domains` tool. Tenant-scope discovery primitive\nthat lets agents (and dashboards) decide whether the brain has\nrelevant knowledge before issuing a billable query. Reuses the\ndomain-coverage shape produced by the knowledge-map service so MCP\nand dashboard see the same numbers."},"DriftResponse":{"properties":{"period_start":{"type":"string","format":"date-time","title":"Period Start"},"period_end":{"type":"string","format":"date-time","title":"Period End"},"total":{"type":"integer","title":"Total","description":"Number of rows on this page"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"items":{"items":{"$ref":"#/components/schemas/DriftRowResponse"},"type":"array","title":"Items"}},"type":"object","required":["period_start","period_end","total","limit","offset","items"],"title":"DriftResponse"},"DriftRowResponse":{"properties":{"model":{"type":"string","title":"Model"},"provider":{"type":"string","title":"Provider"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"call_count":{"type":"integer","title":"Call Count"},"first_seen":{"type":"string","format":"date-time","title":"First Seen"},"last_seen":{"type":"string","format":"date-time","title":"Last Seen"}},"type":"object","required":["model","provider","tenant_id","call_count","first_seen","last_seen"],"title":"DriftRowResponse"},"DriftSectionResponse":{"properties":{"domain":{"type":"string","title":"Domain"},"counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts"},"rows":{"items":{"$ref":"#/components/schemas/DriftSnapshotRowResponse"},"type":"array","title":"Rows"}},"type":"object","required":["domain","counts","rows"],"title":"DriftSectionResponse","description":"Full dashboard payload for one drift domain."},"DriftSnapshotRowResponse":{"properties":{"tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Id"},"connector_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector Id"},"status":{"type":"string","title":"Status"},"error_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Category"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"checked_at":{"type":"string","format":"date-time","title":"Checked At"},"latency_ms":{"type":"integer","title":"Latency Ms"}},"type":"object","required":["status","checked_at","latency_ms"],"title":"DriftSnapshotRowResponse","description":"Latest snapshot for one ``(tenant_id, connector_id)`` target."},"DropboxAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"DropboxAuthUrlResponse","description":"GET /v1/enterprise/connect/dropbox response."},"DropboxFolderItem":{"properties":{"path_lower":{"type":"string","title":"Path Lower"},"path_display":{"type":"string","title":"Path Display"},"name":{"type":"string","title":"Name"},"has_children":{"type":"boolean","title":"Has Children","default":true}},"type":"object","required":["path_lower","path_display","name"],"title":"DropboxFolderItem","description":"A single Dropbox folder returned by the picker.\n\n``has_children`` (v0.5.22 Phase 4 — CC-8) carries the same FE\ncontract as Box: lets the adapter disable the disclosure\ntriangle for empty folders rather than firing an empty\n``fetchChildren`` call.\n\n**Defaults to ``True`` always for Dropbox.** Unlike Box —\nwhich exposes ``item_collection.total_count`` per folder child\nin a single listing — Dropbox's ``/2/files/list_folder`` does\nnot include any \"has children\" hint for child folders.\nDetermining the answer per folder would require an extra\n``/2/files/list_folder`` probe call per child (1 + N calls for\na parent with N folders), adding noticeable picker-open\nlatency for organisations with many top-level folders. The\nfield is wired in the schema for parity + future-proofing\n(e.g. if Dropbox adds a count field, we can populate from\none call), but until then the FE behaves identically to\npre-Phase-4: every folder shows expandable, leaves return\nempty on expand. Mild UX bug, accepted trade-off."},"DropboxFoldersResponse":{"properties":{"folders":{"items":{"$ref":"#/components/schemas/DropboxFolderItem"},"type":"array","title":"Folders"},"selected_scope":{"items":{"type":"string"},"type":"array","title":"Selected Scope"}},"type":"object","required":["folders"],"title":"DropboxFoldersResponse","description":"GET /v1/enterprise/connectors/{id}/dropbox/folders response."},"DropboxScopeRequest":{"properties":{"folder_paths":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Folder Paths"}},"type":"object","title":"DropboxScopeRequest","description":"POST /v1/enterprise/connectors/{id}/dropbox/scope body.\n\nAdmin picks the set of Dropbox folder paths to sync. Each path\nuses Dropbox's canonical ``path_lower`` form (leading slash,\nlowercased segments). Empty string ``\"\"`` is valid — it means\nthe entire user's Dropbox."},"DropboxScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"folder_paths":{"items":{"type":"string"},"type":"array","title":"Folder Paths"},"folder_count":{"type":"integer","title":"Folder Count"}},"type":"object","required":["connector_id","folder_paths","folder_count"],"title":"DropboxScopeResponse","description":"POST /v1/enterprise/connectors/{id}/dropbox/scope response.\n\nDropbox webhooks are app-level, so there are no per-folder\nsubscriptions to create or delete. The response carries only\nthe persisted scope for UI rendering."},"EdgeToPreviousRef":{"properties":{"relationship":{"type":"string","enum":["agreement","disagreement","complementary","superseding"],"title":"Relationship","description":"Edge category from cross_reference_edges.relationship_type. Same string values as the RelationshipType enum."},"peer_chunk_id":{"type":"string","title":"Peer Chunk Id","description":"chunk_id of the previous timeline row this edge connects to."},"confidence":{"anyOf":[{"type":"number","maximum":1.0,"minimum":0.0},{"type":"null"}],"title":"Confidence","description":"Classifier confidence [0.70, 1.0] if the enrichment classifier recorded one. None otherwise."}},"type":"object","required":["relationship","peer_chunk_id"],"title":"EdgeToPreviousRef","description":"Cross-reference edge highlight on a timeline row.\n\nv2 (2026-06-02) replaces the v1 ``relationship_to_next`` field on\n``TimelineNode``. The edge is now a **per-row highlight on the\nreceiving (newer) row** rather than a forward pointer on the older\nrow — this matches the revised frontend semantics where the\ntimeline renders top-down as a chronological list and the edge\npill annotates how each row relates to the immediately preceding\nrow.\n\nPopulated only when a direct ``cross_reference_edges`` row exists\nbetween this chunk and its immediate chronological predecessor in\nthe sorted timeline. The parent field (``TimelineNode.edge_to_\nprevious``) is ``None`` when the two rows are unconnected, or\nwhen the row is the first timeline entry.\n\n``relationship`` matches the underlying ``cross_reference_edges.\nrelationship_type`` column (literal values from\n``common.application.enums.RelationshipType``) — no directional\nsplit. The timeline is chronological, so \"superseding\" between\nrow[i-1] (older) and row[i] (newer) always means \"row[i]\nsupersedes row[i-1]\" by construction.\n\n``confidence`` mirrors the persisted ``cross_reference_edges.\nconfidence`` (floor 0.70). Optional shape leaves room for future\nenrichment variants that may persist edges without a confidence."},"EligibilityError":{"properties":{"error_code":{"type":"string","enum":["ANNUAL_CAP_REACHED","CONCURRENT_CAP_REACHED","REFEREE_ALREADY_REFERRED","SAME_DOMAIN","SELF_REFERRAL"],"title":"Error Code"},"message":{"type":"string","title":"Message"}},"type":"object","required":["error_code","message"],"title":"EligibilityError"},"EmbeddingPipelineResponse":{"properties":{"pending_count":{"type":"integer","title":"Pending Count"},"embedded_24h":{"type":"integer","title":"Embedded 24H"},"latency_p50_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency P50 Seconds"},"latency_p95_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency P95 Seconds"},"hourly_throughput":{"items":{"type":"integer"},"type":"array","title":"Hourly Throughput"}},"type":"object","required":["pending_count","embedded_24h","hourly_throughput"],"title":"EmbeddingPipelineResponse"},"EnterpriseConversationTurnSchema":{"properties":{"role":{"type":"string","enum":["user","assistant"],"title":"Role","description":"Turn role — only user/assistant accepted at v0.5.15."},"content":{"type":"string","maxLength":12000,"minLength":1,"title":"Content","description":"Turn text. Max 12K chars per turn."}},"type":"object","required":["role","content"],"title":"EnterpriseConversationTurnSchema","description":"One prior turn in a conversation thread (v0.5.15 W8 Phase 1).\n\nMirrors the marketplace ``ConversationTurnSchema`` — the two are\ndeliberately separate types per DDD (no cross-domain schema imports)\nbut share the same wire shape. The ``ConversationTurn`` dataclass\nin ``conversation_truncator`` is the unifying internal\nrepresentation."},"EnterpriseQueryRequest":{"properties":{"query":{"type":"string","maxLength":2000,"minLength":1,"title":"Query"},"mode":{"type":"string","title":"Mode","description":"Query mode: fast | standard | deep","default":"standard"},"domain_filter":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain Filter","description":"Restrict to taxonomy domain"},"limit":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Limit","description":"Maximum number of chunks to return. Capped at 10 per v0.5.12 Epic D Q1 ruling to match marketplace behaviour and prevent long-tail noise from reaching the UI.","default":5},"excluded_source_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Excluded Source Ids","description":"v0.5.15 W6: enterprise_connectors.id UUIDs to exclude from retrieval. Pushed to Qdrant as must_not(connector_id ∈ MatchAny). Cross-tenant safe — collection scoping ensures foreign IDs simply do not match."},"excluded_piece_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Excluded Piece Ids","description":"v0.5.15 W6: enterprise_content_pieces.id UUIDs to exclude. Pushed to Qdrant as must_not(content_piece_id ∈ MatchAny). Capped at MAX_EXCLUSION_PIECE_COUNT (1000); beyond that the request returns 400 too_many_exclusions."},"filter_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Filter Mode","description":"v0.9.1: 'include' = treat excluded_source_ids / excluded_piece_ids as INCLUDE lists (chunk must match at least one). 'exclude' or None = treat as EXCLUDE lists (default; backward compat with v0.5.15)."},"conversation_id":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Conversation Id","description":"Optional thread identifier scoping the rewriter cache + (at Phase 2) linking to persisted threads."},"prior_turns":{"anyOf":[{"items":{"$ref":"#/components/schemas/EnterpriseConversationTurnSchema"},"type":"array","maxItems":20},{"type":"null"}],"title":"Prior Turns","description":"Optional conversation history, oldest first. Backend truncates to the last 3-4 turns."},"use_synthesis_envelope":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Use Synthesis Envelope","description":"v_synthesis_migration: when True AND the synthesis_v2_enabled flag resolves on for the caller, the response includes the raw synthesis envelope (``synthesis_envelope``) for the new frontend to render structured callouts/gaps/grounding. Ignored when the flag is off. Default treats the caller as non-envelope-aware (legacy synthesis_text only)."},"candidate_chunk_ids":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":50},{"type":"null"}],"title":"Candidate Chunk Ids","description":"When set, synthesis uses these chunks directly instead of running hybrid_search. All chunks must belong to the authenticated tenant (cross-tenant → 403). Malformed / missing IDs → 400. Empty list → 400 (use absent/None for normal retrieval). Capped at 50 chunks — beyond that the request is rejected (the synthesis context window doesn't fit). Powers the Intelligence Timeline's 'Run synthesis ▾' hand-off."}},"type":"object","required":["query"],"title":"EnterpriseQueryRequest","description":"POST /v1/enterprise/query request body."},"EnterpriseQueryResponse":{"properties":{"query":{"type":"string","title":"Query"},"query_id":{"type":"string","title":"Query Id"},"results":{"items":{"type":"object"},"type":"array","title":"Results"},"result_count":{"type":"integer","title":"Result Count"},"image_results":{"items":{"type":"object"},"type":"array","title":"Image Results","default":[]},"coverage":{"type":"string","title":"Coverage"},"risk_flag":{"type":"object","title":"Risk Flag"},"retrieval_execution_state":{"type":"string","title":"Retrieval Execution State"},"synthesis":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Synthesis"},"synthesis_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Synthesis Model"},"followups":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Followups"},"grounding":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Grounding"},"synthesis_envelope":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Synthesis Envelope"},"latency_ms":{"type":"number","title":"Latency Ms"},"tokens_consumed":{"type":"integer","title":"Tokens Consumed"},"insights":{"items":{"type":"object"},"type":"array","title":"Insights","default":[]},"rewritten_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rewritten Query"},"is_followup":{"type":"boolean","title":"Is Followup","default":false}},"type":"object","required":["query","query_id","results","result_count","coverage","risk_flag","retrieval_execution_state","latency_ms","tokens_consumed"],"title":"EnterpriseQueryResponse","description":"POST /v1/enterprise/query response.\n\n``image_results`` carries presigned S3 URLs (never raw keys) for image\nmatches from the tenant's image_vector index. Empty list for pre-B2\ntenant collections that have no image_vector named vector.\n\nv0.7.x — ``attribution`` field dropped. Was a marketplace-era\n``list[dict]`` of CreatorAttribution entries (60/40 revenue-split\nreceipts). Commit 8669f5a (2026-05-03 ``feat(retrieval): drop\nattribution writes from enterprise query path``) removed the field\nfrom ``SearchContentQuery.handle()``'s return dict but missed this\nschema, leaving the Pydantic model requiring a field the producer\nno longer supplied — every ``/v1/enterprise/query`` call 500'd\nwith a ``ValidationError`` for ~5 days until this fix landed.\nFrontend dashboard search uses ``/v1/enterprise/query/stream`` (SSE,\nno ``response_model``), which is why the bug went undetected;\nsurface area was MCP server + direct API consumers only."},"EnterpriseSnapshotResponse":{"properties":{"active_tenants":{"type":"integer","title":"Active Tenants"},"mrr_total_usd_micros":{"type":"integer","title":"Mrr Total Usd Micros"},"tokens_mtd_total":{"type":"integer","title":"Tokens Mtd Total"},"connectors_active_total":{"type":"integer","title":"Connectors Active Total"},"tenants":{"items":{"$ref":"#/components/schemas/EnterpriseTenantRowResponse"},"type":"array","title":"Tenants"}},"type":"object","required":["active_tenants","mrr_total_usd_micros","tokens_mtd_total","connectors_active_total","tenants"],"title":"EnterpriseSnapshotResponse","description":"Brain Overview snapshot — global totals + per-tenant rows."},"EnterpriseTenantRowResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"status":{"type":"string","title":"Status"},"plan_tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Plan Tier"},"mrr_usd_micros":{"type":"integer","title":"Mrr Usd Micros"},"tokens_mtd":{"type":"integer","title":"Tokens Mtd"},"connectors_active":{"type":"integer","title":"Connectors Active"},"queries_7d":{"type":"integer","title":"Queries 7D"},"created_at":{"type":"string","title":"Created At","default":""},"members_owner":{"type":"integer","title":"Members Owner","default":0},"members_admin":{"type":"integer","title":"Members Admin","default":0},"members_member":{"type":"integer","title":"Members Member","default":0},"corpus_total_gb":{"type":"integer","title":"Corpus Total Gb","default":0},"corpus_used_mb":{"type":"integer","title":"Corpus Used Mb","default":0}},"type":"object","required":["tenant_id","tenant_name","status","mrr_usd_micros","tokens_mtd","connectors_active","queries_7d"],"title":"EnterpriseTenantRowResponse","description":"One tenant in the Brain Overview table."},"EvalRunDetailResponse":{"properties":{"version":{"type":"string","title":"Version"},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"aggregate_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Aggregate Score"},"pass_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pass Count"},"query_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Query Count"},"per_query":{"type":"object","title":"Per Query"},"scorecard_markdown":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scorecard Markdown"}},"type":"object","required":["version"],"title":"EvalRunDetailResponse"},"EvalRunSummaryResponse":{"properties":{"version":{"type":"string","title":"Version"},"prompt":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prompt"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"aggregate_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Aggregate Score"},"pass_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pass Count"},"query_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Query Count"}},"type":"object","required":["version"],"title":"EvalRunSummaryResponse"},"EvalRunsListResponse":{"properties":{"runs":{"items":{"$ref":"#/components/schemas/EvalRunSummaryResponse"},"type":"array","title":"Runs"}},"type":"object","required":["runs"],"title":"EvalRunsListResponse"},"ExpertEdgeSchema":{"properties":{"source_email":{"type":"string","title":"Source Email"},"target_email":{"type":"string","title":"Target Email"},"shared_domains":{"items":{"type":"string"},"type":"array","title":"Shared Domains"},"edge_count":{"type":"integer","title":"Edge Count"}},"type":"object","required":["source_email","target_email","edge_count"],"title":"ExpertEdgeSchema","description":"Edge connecting two experts via shared domains."},"ExpertGraphResponse":{"properties":{"nodes":{"items":{"$ref":"#/components/schemas/ExpertNodeSchema"},"type":"array","title":"Nodes"},"edges":{"items":{"$ref":"#/components/schemas/ExpertEdgeSchema"},"type":"array","title":"Edges"}},"type":"object","required":["nodes","edges"],"title":"ExpertGraphResponse","description":"GET /v1/enterprise/intelligence/expert-graph response."},"ExpertListResponse":{"properties":{"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"experts":{"items":{"$ref":"#/components/schemas/ExpertResultSchema"},"type":"array","title":"Experts"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["experts","total"],"title":"ExpertListResponse","description":"GET /v1/enterprise/intelligence/experts response."},"ExpertNodeSchema":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"authority_score":{"type":"number","title":"Authority Score"},"document_count":{"type":"integer","title":"Document Count"}},"type":"object","required":["email","name","authority_score","document_count"],"title":"ExpertNodeSchema","description":"Node in the expert collaboration graph."},"ExpertProfileSchema":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"document_count":{"type":"integer","title":"Document Count"},"domains":{"items":{"type":"string"},"type":"array","title":"Domains"},"avg_authority":{"type":"number","title":"Avg Authority"},"is_spof":{"type":"boolean","title":"Is Spof"}},"type":"object","required":["email","name","document_count","avg_authority","is_spof"],"title":"ExpertProfileSchema","description":"A single expert as surfaced on the People Intelligence page."},"ExpertResultSchema":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"authority_score":{"type":"number","title":"Authority Score"},"document_count":{"type":"integer","title":"Document Count"},"domains":{"items":{"type":"string"},"type":"array","title":"Domains"},"taxonomy_overlap_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Taxonomy Overlap Pct"}},"type":"object","required":["email","name","authority_score","document_count"],"title":"ExpertResultSchema","description":"Single expert in a discovery result."},"ExpertSummarySchema":{"properties":{"email":{"type":"string","title":"Email"},"name":{"type":"string","title":"Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"document_count":{"type":"integer","title":"Document Count"},"authority_score":{"type":"number","title":"Authority Score"}},"type":"object","required":["email","name","document_count","authority_score"],"title":"ExpertSummarySchema","description":"Top expert for a domain — lightweight summary for the treemap."},"FeatureFlagMutationResponse":{"properties":{"key":{"type":"string","title":"Key"},"current_value":{"type":"string","title":"Current Value"},"has_override":{"type":"boolean","title":"Has Override"},"set_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Set By Email"},"set_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Set At"}},"type":"object","required":["key","current_value","has_override"],"title":"FeatureFlagMutationResponse"},"FeatureFlagResponse":{"properties":{"key":{"type":"string","title":"Key"},"description":{"type":"string","title":"Description"},"kind":{"type":"string","title":"Kind"},"default_value":{"type":"string","title":"Default Value"},"current_value":{"type":"string","title":"Current Value"},"has_override":{"type":"boolean","title":"Has Override"},"override_set_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Set By Email"},"override_set_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Set At"},"override_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Override Reason"},"confirm_phrase_to_override":{"type":"string","title":"Confirm Phrase To Override"},"confirm_phrase_to_clear":{"type":"string","title":"Confirm Phrase To Clear"},"blueprint_node_ids":{"items":{"type":"string"},"type":"array","title":"Blueprint Node Ids"}},"type":"object","required":["key","description","kind","default_value","current_value","has_override","confirm_phrase_to_override","confirm_phrase_to_clear"],"title":"FeatureFlagResponse","description":"One flag row for the GET listing."},"FeatureFlagsListResponse":{"properties":{"flags":{"items":{"$ref":"#/components/schemas/FeatureFlagResponse"},"type":"array","title":"Flags"}},"type":"object","required":["flags"],"title":"FeatureFlagsListResponse"},"FilterPresetListResponse":{"properties":{"presets":{"items":{"$ref":"#/components/schemas/FilterPresetSummary"},"type":"array","title":"Presets"}},"type":"object","required":["presets"],"title":"FilterPresetListResponse","description":"GET /v1/enterprise/search/filter-presets."},"FilterPresetSummary":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"scope":{"type":"string","enum":["personal","tenant"],"title":"Scope"},"mode":{"type":"string","enum":["include","exclude"],"title":"Mode"},"source_ids":{"items":{"type":"string"},"type":"array","title":"Source Ids"},"piece_ids":{"items":{"type":"string"},"type":"array","title":"Piece Ids"},"created_by":{"type":"string","title":"Created By"},"created_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Name"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"published_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published By"},"published_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Published By Name"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"use_count":{"type":"integer","title":"Use Count","default":0}},"type":"object","required":["id","name","scope","mode","created_by","created_at","updated_at"],"title":"FilterPresetSummary","description":"One row in GET /filter-presets and the response body of every\ncreate / patch / publish / unpublish / apply / get endpoint.\n\n``created_by_name`` / ``published_by_name`` are display-only strings\nresolved from tenant_members at read time. ``None`` when the\nunderlying member has been removed from the tenant (the preset row\nsurvives — see UnpublishPresetAction docstring for the orphan\nrationale)."},"FolderItem":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"parent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Parent Id"}},"type":"object","required":["id","name"],"title":"FolderItem","description":"A single Google Drive folder."},"FolderListResponse":{"properties":{"folders":{"items":{"$ref":"#/components/schemas/FolderItem"},"type":"array","title":"Folders"},"selected_folder_ids":{"items":{"type":"string"},"type":"array","title":"Selected Folder Ids"}},"type":"object","required":["folders","selected_folder_ids"],"title":"FolderListResponse","description":"GET /v1/enterprise/connectors/{connector_id}/folders response."},"ForceDeleteRequest":{"properties":{"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason","description":"Operator justification (e.g., 'tos_violation per ticket #9876', 'fraud investigation closed'). Required because force-delete is irreversible: chunks + content_pieces + Qdrant collection are dropped. Audit-trail rows (tenants, tenant_plan, financial ledgers) are preserved."},"bypass_blockers":{"type":"boolean","title":"Bypass Blockers","description":"When True, skips the cancel-blocker pre-check. Use only when an operator is overriding a stuck cancellation (e.g., Stripe outage blocking outstanding_balance check). Default False so the blockers gate the deletion as they would for a customer-initiated cancel.","default":false}},"type":"object","required":["reason"],"title":"ForceDeleteRequest","description":"Body for POST /admin/tenants/{tenant_id}/force-delete."},"ForceDeleteResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"hard_deleted":{"type":"boolean","title":"Hard Deleted"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["success","hard_deleted","reason"],"title":"ForceDeleteResponse","description":"Result of a successful force-delete (or idempotent no-op re-run)."},"ForceInvokeResponse":{"properties":{"job_name":{"type":"string","title":"Job Name"},"triggered_at":{"type":"string","format":"date-time","title":"Triggered At"},"triggered_by":{"type":"string","title":"Triggered By"},"is_destructive":{"type":"boolean","title":"Is Destructive"},"lambda_status_code":{"type":"integer","title":"Lambda Status Code"},"payload":{"type":"object","title":"Payload"},"ecs_task_arn":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ecs Task Arn"}},"type":"object","required":["job_name","triggered_at","triggered_by","is_destructive","lambda_status_code","payload"],"title":"ForceInvokeResponse","description":"Returned to the backoffice after a successful force-invoke."},"ForceRevokeRequest":{"properties":{"reason":{"type":"string","maxLength":40,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"ForceRevokeRequest"},"GDriveAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"GDriveAuthUrlResponse","description":"GET /v1/enterprise/connectors/gdrive/auth response."},"GetAuditResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/_AuditEntry"},"type":"array","title":"Entries"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["entries","page","page_size","total"],"title":"GetAuditResponse"},"GetMemoryResponse":{"properties":{"expertise_domains":{"items":{"$ref":"#/components/schemas/_UserMemoryExpertise"},"type":"array","title":"Expertise Domains"},"role_signal":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role Signal"},"interaction_preferences":{"type":"object","title":"Interaction Preferences"},"engagement_state":{"type":"object","title":"Engagement State"},"personal_supersession_events":{"items":{"type":"object"},"type":"array","title":"Personal Supersession Events"},"tenant_memory":{"$ref":"#/components/schemas/_TenantMemorySliceForUser"},"personalization_paused":{"type":"boolean","title":"Personalization Paused"},"memory_building_stopped":{"type":"boolean","title":"Memory Building Stopped"}},"type":"object","required":["expertise_domains","role_signal","interaction_preferences","engagement_state","personal_supersession_events","tenant_memory","personalization_paused","memory_building_stopped"],"title":"GetMemoryResponse"},"GetSourcesResponse":{"properties":{"sources":{"items":{"type":"object"},"type":"array","title":"Sources"},"page":{"type":"integer","title":"Page"},"page_size":{"type":"integer","title":"Page Size"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["sources","page","page_size","total"],"title":"GetSourcesResponse"},"GmailAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"GmailAuthUrlResponse","description":"GET /v1/enterprise/connect/gmail response (v0.5.24)."},"GmailDisconnectResponse":{"properties":{"status":{"type":"string","title":"Status"},"connector_id":{"type":"string","title":"Connector Id"}},"type":"object","required":["status","connector_id"],"title":"GmailDisconnectResponse","description":"POST /v1/enterprise/connectors/{id}/gmail/disconnect response (v0.5.24)."},"GrandfatheringExtendRequest":{"properties":{"new_grandfathered_until":{"type":"string","format":"date-time","title":"New Grandfathered Until"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"additionalProperties":false,"type":"object","required":["new_grandfathered_until","reason"],"title":"GrandfatheringExtendRequest"},"GrandfatheringMigrateRequest":{"properties":{"target_plan_tier":{"type":"string","enum":["free","team","growth","enterprise"],"title":"Target Plan Tier"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"},"apply_promo_credit_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$"},{"type":"null"}],"title":"Apply Promo Credit Usd"}},"additionalProperties":false,"type":"object","required":["target_plan_tier","reason"],"title":"GrandfatheringMigrateRequest"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HeaderStatsResponse":{"properties":{"thumb_rate_7d":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Thumb Rate 7D"},"total_feedback_30d":{"type":"integer","title":"Total Feedback 30D"},"grounding_failure_rate_per_1k_7d":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Grounding Failure Rate Per 1K 7D"},"grounding_traffic_light":{"type":"string","title":"Grounding Traffic Light","description":"green | yellow | red | unknown"}},"type":"object","required":["thumb_rate_7d","total_feedback_30d","grounding_failure_rate_per_1k_7d","grounding_traffic_light"],"title":"HeaderStatsResponse"},"HealthComponentsSchema":{"properties":{"coverage_breadth":{"type":"number","title":"Coverage Breadth"},"coverage_depth":{"type":"number","title":"Coverage Depth"},"bus_factor":{"type":"number","title":"Bus Factor"},"freshness":{"type":"number","title":"Freshness"},"consistency":{"type":"number","title":"Consistency"}},"type":"object","required":["coverage_breadth","coverage_depth","bus_factor","freshness","consistency"],"title":"HealthComponentsSchema","description":"Five health score components."},"HealthScoreItemSchema":{"properties":{"scope_type":{"type":"string","title":"Scope Type"},"scope_value":{"type":"string","title":"Scope Value"},"composite_score":{"type":"number","title":"Composite Score"},"components":{"$ref":"#/components/schemas/HealthComponentsSchema"},"computed_at":{"type":"string","format":"date-time","title":"Computed At"}},"type":"object","required":["scope_type","scope_value","composite_score","components","computed_at"],"title":"HealthScoreItemSchema","description":"Single health score for a scope."},"HealthScoreResponse":{"properties":{"scores":{"items":{"$ref":"#/components/schemas/HealthScoreItemSchema"},"type":"array","title":"Scores"}},"type":"object","required":["scores"],"title":"HealthScoreResponse","description":"GET /v1/enterprise/intelligence/health response."},"IdentityResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"saml":{"$ref":"#/components/schemas/IdentitySamlConfigResponse"},"scim":{"$ref":"#/components/schemas/IdentityScimSectionResponse"}},"additionalProperties":false,"type":"object","required":["tenant_id","saml","scim"],"title":"IdentityResponse","description":"Per-tenant identity activation envelope."},"IdentitySamlConfigResponse":{"properties":{"configured":{"type":"boolean","title":"Configured"},"active":{"type":"boolean","title":"Active"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"dns_verified":{"type":"boolean","title":"Dns Verified","default":false},"dns_verified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Dns Verified At"},"saml_enabled":{"type":"boolean","title":"Saml Enabled","default":false},"idp_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Type"},"idp_entity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Entity Id"},"idp_acs_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Acs Url"},"idp_signin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Signin Url"},"idp_certificate_fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Certificate Fingerprint"}},"additionalProperties":false,"type":"object","required":["configured","active"],"title":"IdentitySamlConfigResponse","description":"SAML configuration snapshot for a single tenant — read-only.\n\nMirrors the customer-facing ``SamlConfigResponse`` in\n``saml_routes.py`` plus the activation-state fields a backoffice\noperator needs to answer \"did this tenant configure SAML?\" without\nreading the CapabilityGrid (which answers \"does the plan grant\nSAML?\" — a different question).\n\n``configured`` is true iff a row exists in\n``enterprise_tenant_domain_policies`` for this tenant; ``active``\nis true iff ``saml_enabled=true AND dns_verified=true`` — the\nruntime gate ``_lookup_active_saml_policy`` uses."},"IdentityScimSectionResponse":{"properties":{"implemented":{"type":"boolean","title":"Implemented","default":true},"active_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Active Tokens"},"provisioned_users_active":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Provisioned Users Active"},"last_activity_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Activity At"}},"additionalProperties":false,"type":"object","title":"IdentityScimSectionResponse","description":"SCIM activation snapshot — populated from the SCIM tables now that\nthe SCIM workstream is merged. ``implemented`` stays in the shape for\nbackwards compatibility with the placeholder release; the frontend\ntreats ``True`` as \"render real counters\" and ``False`` as \"show\nnot-implemented tile\"."},"InfraSpendByService":{"properties":{"service":{"type":"string","title":"Service","description":"``infra_usage_daily.service`` value — e.g. ``s3``, ``nat-gateway``, ``lambda``, ``rds-aurora``, ``stripe``."},"total_cost_usd":{"type":"string","title":"Total Cost Usd","description":"Sum of ``cost_estimate_usd`` for this service over the window. Currency is USD; precision is 6 decimal places."},"row_count":{"type":"integer","title":"Row Count","description":"Number of ``infra_usage_daily`` rows aggregated into ``total_cost_usd``. One row per (date, dimension). Useful for surfacing partial-coverage windows in the UI."},"pct_of_total":{"type":"number","title":"Pct Of Total","description":"Share of window-wide infra spend (0–100). Excludes LLM spend (the dashboard joins LLM + infra client-side)."}},"type":"object","required":["service","total_cost_usd","row_count","pct_of_total"],"title":"InfraSpendByService","description":"One row of the per-service infra spend rollup.\n\n``total_cost_usd`` is the sum of ``cost_estimate_usd`` over the\nwindow for this service; ``pct_of_total`` is its share of the\nwindow-wide infra spend (LLM spend is NOT included — the dashboard\njoins this response with the live-LLM response from PR #610\nclient-side). Rows with ``cost_estimate_usd IS NULL`` (Qdrant\nsnapshot rows, services with no catalog rate yet) are excluded\nfrom the totals — the dashboard surfaces them on a separate\npanel."},"InfraSpendResponse":{"properties":{"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"UTC timestamp the response was assembled."},"window_start":{"type":"string","format":"date","title":"Window Start","description":"Inclusive start of the window (UTC date)."},"window_end":{"type":"string","format":"date","title":"Window End","description":"Inclusive end of the window (UTC date, today)."},"window_days":{"type":"integer","title":"Window Days","description":"Window size in days (max 365)."},"total_cost_usd":{"type":"string","title":"Total Cost Usd","description":"Sum of ``cost_estimate_usd`` across every service in the window. Excludes Qdrant + any rows missing a catalog rate."},"services":{"items":{"$ref":"#/components/schemas/InfraSpendByService"},"type":"array","title":"Services","description":"One row per service, sorted DESC by ``total_cost_usd``. Empty list when no rows landed in the window — operators read this as 'nightly pulls didn't fire' rather than '$0 of actual spend'."}},"type":"object","required":["generated_at","window_start","window_end","window_days","total_cost_usd","services"],"title":"InfraSpendResponse","description":"Top-level response for ``GET /v1/admin/cost-catalog/infra-spend``."},"InfrastructureEntryView":{"properties":{"staleness_days":{"type":"integer","title":"Staleness Days","description":"Days since ``verified_at``. Catalog viewer renders green ≤90, yellow 91-180, red >180. Computed server-side so xlsx export carries the same value the live viewer displays."},"provider":{"type":"string","title":"Provider"},"service":{"type":"string","title":"Service"},"pricing":{"$ref":"#/components/schemas/Pricing"},"source_url":{"type":"string","title":"Source Url","description":"Provider pricing page URL — opens in the backoffice 'verify now' button."},"verified_at":{"type":"string","format":"date","title":"Verified At","description":"Last date a human checked the rate against the provider page. Backoffice flags >90 days as stale."},"call_sites":{"items":{"type":"string"},"type":"array","title":"Call Sites","description":"file:line pointers to where the cost is incurred. Optional for infra entries (provisioned-not-called)."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Free-text caveats / migration history."},"aws_service":{"type":"string","title":"Aws Service","description":"Canonical AWS service slug — 'rds-aurora', 'ecs-fargate', 's3', 'kms', etc."},"region":{"type":"string","title":"Region","description":"Primary region. Multi-region entries add extra rows.","default":"eu-central-1"}},"type":"object","required":["staleness_days","provider","service","pricing","source_url","verified_at","aws_service"],"title":"InfrastructureEntryView","description":"Infrastructure entry enriched with ``staleness_days``."},"IngestAnywayRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason","description":"Optional operator justification for overriding the gate."}},"type":"object","title":"IngestAnywayRequest","description":"POST /v1/enterprise/library/{piece_id}/ingest-anyway request body.\n\nBody is optional — clients may post ``{}`` or omit the body entirely.\nWhen supplied, ``reason`` is the operator's free-text justification,\npersisted on the audit log row and on the piece's gate metadata so\na future reviewer can see why this override happened.\n\nThe override applies to the quality gate ONLY. Secret redaction is\nnot overridable; redacted secrets stay redacted regardless of this\nrequest body. See ``backend/app/domains/ingestion/.../secret_redactor.py``\nand CLAUDE.md §v0.6.2 for the full contract."},"IngestAnywayResponse":{"properties":{"piece_id":{"type":"string","title":"Piece Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["piece_id","status"],"title":"IngestAnywayResponse","description":"POST /v1/enterprise/library/{piece_id}/ingest-anyway response."},"InstrumentEnabledStateResponse":{"properties":{"instrument":{"type":"string","title":"Instrument"},"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["instrument","enabled"],"title":"InstrumentEnabledStateResponse","description":"``GET /v1/admin/surveys/instruments/{instrument}/enabled``."},"InstrumentMetricsRowResponse":{"properties":{"instrument":{"type":"string","title":"Instrument"},"response_count":{"type":"integer","title":"Response Count"},"dismissal_count":{"type":"integer","title":"Dismissal Count"},"response_rate":{"type":"number","title":"Response Rate"},"score_average":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score Average"},"distinct_responders":{"type":"integer","title":"Distinct Responders"}},"type":"object","required":["instrument","response_count","dismissal_count","response_rate","distinct_responders"],"title":"InstrumentMetricsRowResponse","description":"Per-instrument self-tuning row over a rolling window."},"IntelligenceFeedItemSchema":{"properties":{"id":{"type":"string","title":"Id"},"signal_type":{"type":"string","title":"Signal Type"},"severity":{"type":"string","title":"Severity"},"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"person_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","signal_type","severity","title","description","created_at"],"title":"IntelligenceFeedItemSchema","description":"One auto-generated item in the live signals feed."},"InviteMetadataResponse":{"properties":{"state":{"type":"string","enum":["valid","expired","revoked","exhausted","not_found"],"title":"State"},"workspace_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Workspace Name"},"inviter_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inviter Email"},"inviter_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Inviter Name"},"role":{"anyOf":[{"type":"string","enum":["admin","member"]},{"type":"null"}],"title":"Role"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["state"],"title":"InviteMetadataResponse","description":"Public invite lookup DTO for the unauthenticated accept page."},"InviteSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"role_on_accept":{"type":"string","enum":["admin","member"],"title":"Role On Accept"},"email_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email Hint"},"max_uses":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Uses"},"used_count":{"type":"integer","title":"Used Count"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"rejected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rejected At"},"resend_count":{"type":"integer","title":"Resend Count","default":0},"status":{"type":"string","enum":["pending","accepted","rejected","expired","revoked"],"title":"Status","description":"Server-computed lifecycle status (see ``compute_invite_status``).","readOnly":true}},"type":"object","required":["id","role_on_accept","used_count","created_at","status"],"title":"InviteSummary","description":"Listing DTO — deliberately omits the raw token.\n\n``status`` is **computed server-side** from the precedence rules in\n``app.domains.enterprise.application.services.invite_status`` —\ndrives the Members page affordances (Revoke / Resend) on the\nenterprise dashboard. The five possible values mirror the\n``InviteStatus`` literal: ``pending`` (Revoke) / ``accepted``\n(filtered client-side off the pending list) / ``rejected`` (hard\nbounce — Revoke + Resend) / ``expired`` (Revoke + Resend) /\n``revoked`` (filtered server-side by ``ListInvites``; can still\nappear on the resend endpoint's 409 path).\n\n``rejected_at`` is exposed alongside ``status`` so the frontend can\nshow \"bounced 2 days ago\" copy. ``resend_count`` lets the frontend\nrate-limit the Resend button after N retries without round-tripping\nthe action."},"IssueCreditRequest":{"properties":{"amount_kt":{"type":"integer","maximum":10000000.0,"exclusiveMinimum":0.0,"title":"Amount Kt","description":"Gross kT to add to the tenant balance. Hard-capped at 10,000,000 kT (~$30). Larger credits require a one-off database operation outside this endpoint."},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason","description":"Operator-supplied justification — recorded in tenant_credits.reason and the audit log. At v0.2 this carries the human attribution since the shared admin identity lacks per-operator UUIDs (e.g., \"Antonis CS: refund per ticket #1234\")."}},"type":"object","required":["amount_kt","reason"],"title":"IssueCreditRequest","description":"Body for POST /admin/tenants/{tenant_id}/issue-credit."},"IssueCreditResponse":{"properties":{"credit_id":{"type":"string","title":"Credit Id"},"balance_after":{"type":"integer","title":"Balance After"}},"type":"object","required":["credit_id","balance_after"],"title":"IssueCreditResponse","description":"Result of a successful credit issuance."},"KillSwitchCreateIn":{"properties":{"template_id":{"type":"string","maxLength":80,"minLength":1,"title":"Template Id","description":"Bare lifecycle template id, e.g. 'daily-pool-exhausted'"},"reason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reason","description":"Optional free-text reason — shown in the audit row"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id","description":"Omit for a global kill switch; set for tenant-scoped"}},"type":"object","required":["template_id"],"title":"KillSwitchCreateIn","description":"Disable-template payload from the backoffice toggle."},"KillSwitchListOut":{"properties":{"items":{"items":{"$ref":"#/components/schemas/KillSwitchOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"KillSwitchListOut"},"KillSwitchOut":{"properties":{"template_id":{"type":"string","title":"Template Id","description":"Bare lifecycle template id"},"disabled_at":{"type":"string","format":"date-time","title":"Disabled At"},"disabled_by":{"type":"string","title":"Disabled By"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"scope":{"type":"string","title":"Scope","description":"'global' or 'tenant'"}},"type":"object","required":["template_id","disabled_at","disabled_by","scope"],"title":"KillSwitchOut","description":"One disabled-template row, surfaced to the backoffice."},"KnowledgeMapResponse":{"properties":{"domains":{"items":{"$ref":"#/components/schemas/DomainCoverageSchema"},"type":"array","title":"Domains"},"org_health":{"$ref":"#/components/schemas/OrgHealthSchema"}},"type":"object","required":["domains","org_health"],"title":"KnowledgeMapResponse","description":"GET /v1/enterprise/knowledge-map response."},"KtPoolOverrideRequest":{"properties":{"daily_kt_pool_per_seat":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Kt Pool Per Seat","description":"Per-tenant override of the daily kT pool. Must be > 0 if set; pass null to clear the override and revert to the plan default. Writes to ``tenant_plan.override_daily_kt_pool_per_seat``."},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"additionalProperties":false,"type":"object","required":["reason"],"title":"KtPoolOverrideRequest","description":"Lock-additive — see dispatch-log OCI-5."},"LLMEntryView":{"properties":{"staleness_days":{"type":"integer","title":"Staleness Days","description":"Days since ``verified_at``. Catalog viewer renders green ≤90, yellow 91-180, red >180. Computed server-side so xlsx export carries the same value the live viewer displays."},"provider":{"type":"string","title":"Provider"},"service":{"type":"string","title":"Service"},"pricing":{"$ref":"#/components/schemas/Pricing"},"source_url":{"type":"string","title":"Source Url","description":"Provider pricing page URL — opens in the backoffice 'verify now' button."},"verified_at":{"type":"string","format":"date","title":"Verified At","description":"Last date a human checked the rate against the provider page. Backoffice flags >90 days as stale."},"call_sites":{"items":{"type":"string"},"type":"array","title":"Call Sites","description":"file:line pointers to where the cost is incurred. Optional for infra entries (provisioned-not-called)."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Free-text caveats / migration history."},"workload":{"type":"string","title":"Workload","description":"Matches llm_call_logs.workload. Same provider+service across workloads = multiple entries."},"region_scope":{"type":"string","enum":["all","global","eu_us"],"title":"Region Scope","description":"Which region(s) actually route to this provider. ('global' = APAC/Alibaba, 'eu_us' = Anthropic/OpenAI direct, 'all' = same everywhere).","default":"all"},"stripe_meter_dimension":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Meter Dimension","description":"Stripe Meter dimension name when this workload bills customers (kT-bearing). None for internal-only."}},"type":"object","required":["staleness_days","provider","service","pricing","source_url","verified_at","workload"],"title":"LLMEntryView","description":"LLM entry enriched with ``staleness_days``."},"ListApiKeysResponse":{"properties":{"keys":{"items":{"$ref":"#/components/schemas/ApiKeySummary"},"type":"array","title":"Keys"},"active_count":{"type":"integer","title":"Active Count"},"limit":{"type":"integer","title":"Limit"}},"type":"object","required":["keys","active_count","limit"],"title":"ListApiKeysResponse"},"ListInvitesResponse":{"properties":{"invites":{"items":{"$ref":"#/components/schemas/InviteSummary"},"type":"array","title":"Invites"}},"type":"object","required":["invites"],"title":"ListInvitesResponse"},"ListPartsRequest":{"properties":{"upload_id":{"type":"string","maxLength":2048,"minLength":1,"title":"Upload Id"}},"type":"object","required":["upload_id"],"title":"ListPartsRequest","description":"Request body for the list-parts endpoint."},"ListPartsResponse":{"properties":{"parts":{"items":{"$ref":"#/components/schemas/ListedPartResponse"},"type":"array","title":"Parts"}},"type":"object","required":["parts"],"title":"ListPartsResponse","description":"Response body for the list-parts endpoint."},"ListPatsResponse":{"properties":{"tokens":{"items":{"$ref":"#/components/schemas/PatSummary"},"type":"array","title":"Tokens","description":"Sanitized PATs owned by the calling member."}},"type":"object","title":"ListPatsResponse","description":"Response payload for the list endpoint."},"ListReferralsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ReferralSummary"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"ListReferralsResponse"},"ListServiceAccountsResponse":{"properties":{"accounts":{"items":{"$ref":"#/components/schemas/ServiceAccountResponse"},"type":"array","title":"Accounts"}},"additionalProperties":false,"type":"object","required":["accounts"],"title":"ListServiceAccountsResponse"},"ListSessionsResponse":{"properties":{"sessions":{"items":{"$ref":"#/components/schemas/SessionRow"},"type":"array","title":"Sessions"}},"type":"object","required":["sessions"],"title":"ListSessionsResponse"},"ListThreadsResponse":{"properties":{"threads":{"items":{"$ref":"#/components/schemas/ThreadSummaryResponse"},"type":"array","title":"Threads"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"ISO 8601 last_query_at of the last returned row. Pass back as ``cursor`` query param to fetch the next page. Null when there are no more pages."}},"type":"object","required":["threads"],"title":"ListThreadsResponse","description":"GET /v1/enterprise/threads response."},"ListedPartResponse":{"properties":{"part_number":{"type":"integer","title":"Part Number"},"etag":{"type":"string","title":"Etag"},"size":{"type":"integer","title":"Size"}},"type":"object","required":["part_number","etag","size"],"title":"ListedPartResponse","description":"One part already in S3."},"LiveSpendByTenant":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name","description":"Joined from ``tenants.name``; ``None`` if the tenant row is missing (rare; usually a hard-deleted tenant)."},"total_usd":{"type":"number","title":"Total Usd"},"call_count":{"type":"integer","title":"Call Count"},"top_workload":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Top Workload","description":"Highest-spend workload for this tenant in the window. ``None`` only if the tenant has zero calls (shouldn't appear in the list at all in that case)."}},"type":"object","required":["tenant_id","total_usd","call_count"],"title":"LiveSpendByTenant","description":"One row of the per-tenant spend table."},"LiveSpendByWorkload":{"properties":{"workload":{"type":"string","title":"Workload"},"provider":{"type":"string","title":"Provider","description":"Provider from the catalog (``llm.yml``) when a matching workload entry exists; otherwise echoes the provider stamped on the call log row."},"service":{"type":"string","title":"Service","description":"Catalog ``service`` (model SKU, e.g. ``gpt-5-mini``) when a matching catalog row exists; otherwise the call log's ``model`` column."},"call_count":{"type":"integer","title":"Call Count"},"total_usd":{"type":"number","title":"Total Usd"},"median_per_call_usd":{"type":"number","title":"Median Per Call Usd"},"pct_of_total":{"type":"number","title":"Pct Of Total","description":"Percentage 0-100 of total window spend this workload represents. Sums to ~100 across all rows."}},"type":"object","required":["workload","provider","service","call_count","total_usd","median_per_call_usd","pct_of_total"],"title":"LiveSpendByWorkload","description":"One row of the per-workload spend table."},"LiveSpendDailyPoint":{"properties":{"date":{"type":"string","format":"date","title":"Date","description":"UTC calendar day."},"total_usd":{"type":"number","title":"Total Usd"},"workload_breakdown":{"additionalProperties":{"type":"number"},"type":"object","title":"Workload Breakdown","description":"Per-workload dollar split for the day, keyed by workload name. Renders the optional stacked-line view when the chart toggle is on. Empty dict on days with zero spend."}},"type":"object","required":["date","total_usd"],"title":"LiveSpendDailyPoint","description":"One day of the time-series chart.\n\nField is named ``date`` over the wire (the frontend chart library\nreads ``point.date``) but the type-annotation uses the renamed\n``_Date`` import to dodge the pydantic shadowing error you'd get\nif a field name == its type name."},"LiveSpendResponse":{"properties":{"window_start":{"type":"string","format":"date","title":"Window Start","description":"Inclusive start of the spend window (UTC day)."},"window_end":{"type":"string","format":"date","title":"Window End","description":"Inclusive end of the spend window (UTC day)."},"generated_at":{"type":"string","format":"date-time","title":"Generated At","description":"UTC timestamp the response was assembled."},"stats":{"$ref":"#/components/schemas/LiveSpendStat"},"by_workload":{"items":{"$ref":"#/components/schemas/LiveSpendByWorkload"},"type":"array","title":"By Workload"},"by_tenant":{"items":{"$ref":"#/components/schemas/LiveSpendByTenant"},"type":"array","title":"By Tenant"},"daily":{"items":{"$ref":"#/components/schemas/LiveSpendDailyPoint"},"type":"array","title":"Daily"}},"type":"object","required":["window_start","window_end","generated_at","stats","by_workload","by_tenant","daily"],"title":"LiveSpendResponse","description":"Top-level response for ``GET /v1/admin/cost-catalog/live``."},"LiveSpendStat":{"properties":{"total_usd":{"type":"number","title":"Total Usd","description":"Total LLM spend over the requested window, in USD. Sourced from ``SUM(llm_call_logs.cost_usd_micros) / 1e6``."},"total_calls":{"type":"integer","title":"Total Calls","description":"Total number of LLM calls in the window. Drives the $/answer median denominator and the dashboard stat strip."},"top_workload":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Top Workload","description":"Highest-spend ``llm_call_logs.workload`` value in the window. ``None`` when the window has zero calls."},"top_tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Top Tenant Id","description":"Highest-spend tenant id (UUID string). ``None`` when the window has zero calls."},"top_tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Top Tenant Name","description":"Human-readable name for ``top_tenant_id`` joined from ``tenants.name``. ``None`` if the tenant row is missing."},"median_cost_per_query_usd":{"type":"number","title":"Median Cost Per Query Usd","description":"Median per-call cost in USD across the window. Approximated with a deterministic per-call sort then index-50% because Postgres ``percentile_cont`` over the full call log was deemed unnecessary for an operator dashboard."}},"type":"object","required":["total_usd","total_calls","median_cost_per_query_usd"],"title":"LiveSpendStat","description":"Top-of-page summary numbers for the live-spend panel."},"LlmCallResponse":{"properties":{"call_id":{"type":"string","format":"uuid","title":"Call Id"},"workload":{"type":"string","title":"Workload"},"provider":{"type":"string","title":"Provider"},"model":{"type":"string","title":"Model"},"unit_type":{"type":"string","title":"Unit Type"},"input_units":{"type":"integer","title":"Input Units"},"output_units":{"type":"integer","title":"Output Units"},"cached_input_units":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Cached Input Units"},"image_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Image Count"},"image_total_pixels":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Image Total Pixels"},"cost_usd_micros":{"type":"integer","title":"Cost Usd Micros"},"cost_usd":{"type":"string","title":"Cost Usd","description":"Cost rendered as USD with 6 fractional digits, e.g. '0.000494'"},"cost_breakdown":{"type":"string","title":"Cost Breakdown","description":"Educational format string showing the arithmetic — '<units> × $<rate>/MTok = $<cost>'."},"latency_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Latency Ms"},"finish_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Finish Reason"},"rate_card_effective_from":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rate Card Effective From"},"rate_lookup_failed":{"type":"boolean","title":"Rate Lookup Failed"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["call_id","workload","provider","model","unit_type","input_units","output_units","cost_usd_micros","cost_usd","cost_breakdown","rate_lookup_failed","created_at"],"title":"LlmCallResponse","description":"One LLM call inside a query drill-down.\n\n``cost_breakdown`` is the educational format string\n\"1,234 tokens × $0.40/MTok = $0.000494\". Show the math, not just\nthe result. Backoffice only."},"ManifestEntryOut":{"properties":{"template_id":{"type":"string","title":"Template Id"},"subject":{"type":"string","title":"Subject"},"preview_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Text"},"html":{"type":"string","title":"Html"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Text"},"required_tokens":{"items":{"type":"string"},"type":"array","title":"Required Tokens"}},"type":"object","required":["template_id","subject","html","required_tokens"],"title":"ManifestEntryOut","description":"Read-only projection of the compiled manifest entry, served\nalongside the live override so the backoffice editor can offer\n'edit from current'."},"ManualConvertRequest":{"properties":{"reason":{"type":"string","maxLength":200,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"ManualConvertRequest"},"ManualOffboardRequest":{"properties":{"target_email":{"type":"string","format":"email","title":"Target Email"},"reason":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Reason"}},"type":"object","required":["target_email"],"title":"ManualOffboardRequest"},"ManualOffboardResponse":{"properties":{"event_id":{"type":"string","title":"Event Id"},"status":{"type":"string","enum":["received","processing","completed","failed"],"title":"Status"},"revoked_session_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Revoked Session Count"},"revoked_pat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Revoked Pat Count"},"deactivated_sa_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Deactivated Sa Count"}},"type":"object","required":["event_id","status"],"title":"ManualOffboardResponse"},"MarketingPricingContentSchema":{"properties":{"available":{"type":"boolean","title":"Available"},"content":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Content"}},"type":"object","required":["available"],"title":"MarketingPricingContentSchema","description":"Wire shape for `/api/billing/marketing-content`.\n\n``content`` is the marketing site's JSON payload verbatim (whatever\nshape it ships). The modal does null-safe access at each leaf."},"MembershipResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"email":{"type":"string","title":"Email"},"role":{"type":"string","enum":["owner","admin","member"],"title":"Role"},"status":{"type":"string","enum":["active","suspended"],"title":"Status"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"}},"type":"object","required":["id","tenant_id","email","role","status"],"title":"MembershipResponse","description":"Subset of TenantMember returned after invite acceptance."},"MessageResponse":{"properties":{"id":{"type":"string","title":"Id"},"role":{"type":"string","enum":["user","assistant"],"title":"Role"},"content":{"type":"string","title":"Content"},"citations":{"anyOf":[{"items":{"type":"object"},"type":"array"},{"type":"null"}],"title":"Citations"},"rewritten_query":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rewritten Query"},"query_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Type"},"excluded_source_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Excluded Source Ids"},"excluded_piece_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Excluded Piece Ids"},"query_log_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Log Id"},"aborted_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Aborted At"},"grounding":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Grounding"},"followups":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Followups"},"synthesis_metadata":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Synthesis Metadata"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","role","content","created_at"],"title":"MessageResponse","description":"One turn in the restoration response.\n\nMirrors ConversationMessage column shape. Optional fields are\npresent-or-null on the wire (frontend uses null checks to decide\nwhether to render rewritten-query badges, grounding warnings,\netc.)."},"MintApiKeyRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Name","description":"Human-readable label, e.g. 'Production Search Key'"},"scope":{"type":"string","enum":["read","write"],"title":"Scope","description":"API key scope. Read keys can call retrieval + listing endpoints; write keys can additionally call mutating endpoints (upload, sync, etc.). Admin actions always require Clerk SSO regardless of scope.","default":"read"}},"additionalProperties":false,"type":"object","title":"MintApiKeyRequest","description":"Body for ``POST /v1/enterprise/api-keys``."},"MintApiKeyResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Name"},"key_prefix":{"type":"string","title":"Key Prefix"},"scope":{"type":"string","enum":["read","write"],"title":"Scope"},"created_by":{"type":"string","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"revoked_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Revoked By"},"key":{"type":"string","title":"Key","description":"Full plaintext API key. SAVE THIS NOW — it is not stored and cannot be re-displayed. Loss requires revocation + minting a new key."},"warning":{"type":"string","title":"Warning","default":"Save this key now. It will not be shown again."}},"type":"object","required":["id","key_prefix","scope","created_by","created_at","key"],"title":"MintApiKeyResponse","description":"Mint response includes the full plaintext key — shown ONCE."},"MintPatRequest":{"properties":{"name":{"type":"string","maxLength":64,"minLength":1,"title":"Name","description":"Human-readable label for the token, e.g. 'Laptop' or 'CI build'. Must be unique among the user's non-revoked tokens; revoking a token frees its name for re-use.","examples":["Laptop"]},"expires_in_days":{"anyOf":[{"type":"integer","maximum":365.0,"minimum":1.0},{"type":"null"}],"title":"Expires In Days","description":"Optional clock expiry in days. NULL → no expiry. Must be a positive integer ≤ 365 if supplied.","examples":[365]},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"B-FE-11: optional closed-set scope list (READ / WRITE / ADMIN). NULL/omitted → caller will mint a legacy-shape PAT (no scopes recorded). Frontend FE-12 (PR #325) deferred the scopes picker — backend ships the contract first so the picker wires in without a coordinated release.","examples":[["READ","WRITE"]]}},"additionalProperties":false,"type":"object","required":["name"],"title":"MintPatRequest","description":"Body for ``POST /v1/enterprise/me/personal-access-tokens``."},"MintPatResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"UUID of the PAT row."},"name":{"type":"string","title":"Name","description":"Human-readable label."},"key_prefix":{"type":"string","title":"Key Prefix","description":"First 16 chars of the plaintext key (``qlv_pat_<8 chars>``) — displayed in CLI / dashboard for identification.","examples":["qlv_pat_a1b2c3d4"]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"B-FE-11: scopes attached to this PAT, or NULL on legacy rows minted before the column existed. See ``ALLOWED_PAT_SCOPES`` for the closed set.","examples":[["READ","WRITE"]]},"key":{"type":"string","title":"Key","description":"Full plaintext personal access token. SAVE THIS NOW — it is not stored and cannot be re-displayed. Loss requires revocation + minting a new token.","examples":["qlv_pat_aBcDeFgHiJkLmNoPqRsTuVwXyZ0123456789-_aBcDeFg"]},"warning":{"type":"string","title":"Warning","description":"Constant reminder string for the CLI / dashboard.","default":"Save this token now. It will not be shown again."}},"type":"object","required":["id","name","key_prefix","created_at","key"],"title":"MintPatResponse","description":"Mint response includes the full plaintext key — shown ONCE."},"ModelBreakdownResponse":{"properties":{"synthesis_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Synthesis Model"},"thumbs_up":{"type":"integer","title":"Thumbs Up"},"thumbs_down":{"type":"integer","title":"Thumbs Down"},"total":{"type":"integer","title":"Total"},"thumb_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Thumb Rate"}},"type":"object","required":["synthesis_model","thumbs_up","thumbs_down","total","thumb_rate"],"title":"ModelBreakdownResponse"},"MyAuditLogEntryResponse":{"properties":{"id":{"type":"string","title":"Id"},"piece_id":{"type":"string","title":"Piece Id"},"mutation_source":{"type":"string","title":"Mutation Source"},"mutated_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mutated By Email"},"mutated_by_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mutated By User Id"},"permission_emails_before":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Permission Emails Before"},"permission_emails_after":{"items":{},"type":"array","title":"Permission Emails After"},"resolution_mode_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Mode Before"},"resolution_mode_after":{"type":"string","title":"Resolution Mode After"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"mutated_at":{"type":"string","format":"date-time","title":"Mutated At"}},"type":"object","required":["id","piece_id","mutation_source","permission_emails_after","resolution_mode_after","mutated_at"],"title":"MyAuditLogEntryResponse","description":"One ``permission_audit_log`` row scoped to the caller's tenant.\n\nShape matches the operator variant (``PermissionAuditLogEntryResponse``)\nminus the ``tenant_id`` field — the tail is always single-tenant on\nthis endpoint, so the redundant id would just bloat the payload."},"MyAuditTailResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id","description":"Caller's tenant — echoed so the frontend can sanity-check the request was scoped correctly before rendering."},"entries":{"items":{"$ref":"#/components/schemas/MyAuditLogEntryResponse"},"type":"array","title":"Entries"},"limit":{"type":"integer","title":"Limit"}},"type":"object","required":["tenant_id","entries","limit"],"title":"MyAuditTailResponse","description":"B-FE-7 — tenant-scoped audit-tail payload.\n\nReturned by ``GET /v1/enterprise/permission-backfill/my-audit-tail``.\nOrdered ``mutated_at DESC``; same 100-row default + 100-row cap as\nthe admin variant downsized for the tenant audience (the operator\nsurface allowed up to 500)."},"MyBackfillStatusResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id","description":"Caller's tenant — always derived from the session, never from a query parameter."},"phase":{"type":"string","enum":["not_started","backfill_in_progress","backfill_complete"],"title":"Phase"},"pieces_total":{"type":"integer","title":"Pieces Total","description":"Total pieces in this tenant's corpus (across every connector + uploads). Sourced from the ``enterprise_content_pieces`` data plane, not the progress-row counters — the data plane is the source of truth even when a runner-crash leaves the progress row stale."},"pieces_processed":{"type":"integer","title":"Pieces Processed"},"pieces_qdrant_updated":{"type":"integer","title":"Pieces Qdrant Updated"},"pct_complete":{"type":"number","title":"Pct Complete","description":"``pieces_backfilled / pieces_total * 100`` clamped to [0.0, 100.0]. ``0.0`` when ``pieces_total == 0`` (no corpus yet) — never NaN."},"last_advanced_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Advanced At","description":"Most recent of ``last_error_at`` / ``completed_at`` / ``started_at``. ``None`` when ``phase='not_started'``."},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"errors_count":{"type":"integer","title":"Errors Count","description":"Number of ``permission_audit_log`` rows for this tenant whose ``mutation_source = 'backfill_error'``. Always 0 today (the runner currently writes errors to ``permission_backfill_progress.last_error``, not the audit log) — reserved for the v1.4 audit-error surface."},"per_connector":{"items":{"$ref":"#/components/schemas/TenantBackfillConnectorResponse"},"type":"array","title":"Per Connector","description":"Per-connector breakdown, alphabetical by ``connector_type`` then ``connector_id``. Empty when ``phase='not_started'``."}},"type":"object","required":["tenant_id","phase","pieces_total","pieces_processed","pieces_qdrant_updated","pct_complete","errors_count"],"title":"MyBackfillStatusResponse","description":"B-FE-7 — tenant-scoped backfill status payload.\n\nReturned by ``GET /v1/enterprise/permission-backfill/my-status``.\nFrontend contract for the FE-9 tenant-admin permission dashboard.\n\nPhase machine\n-------------\n* ``not_started``         — no Phase 4 ``permission_backfill_progress``\n                            row for this tenant. ``last_advanced_at``\n                            and ``completed_at`` are NULL, every\n                            counter is zero, and ``per_connector`` is\n                            empty.\n* ``backfill_in_progress``— progress row exists; ``completed_at`` is\n                            NULL.\n* ``backfill_complete``   — progress row exists; ``completed_at`` is\n                            NOT NULL.\n\nThe frontend renders the phase pill from this field alone; do not\nbranch on ``pieces_*`` counters to infer state."},"NPSMetricsResponse":{"properties":{"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"overall":{"$ref":"#/components/schemas/NPSSegmentMetricsResponse"},"by_billing_state":{"items":{"$ref":"#/components/schemas/NPSSegmentMetricsResponse"},"type":"array","title":"By Billing State"},"response_rate":{"type":"number","title":"Response Rate"},"dismissal_count":{"type":"integer","title":"Dismissal Count"}},"type":"object","required":["window_start","window_end","overall","by_billing_state","response_rate","dismissal_count"],"title":"NPSMetricsResponse","description":"``GET /v1/admin/surveys/nps/metrics`` response."},"NPSScoreCountResponse":{"properties":{"score":{"type":"integer","title":"Score"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["score","count"],"title":"NPSScoreCountResponse","description":"One row in the NPS 11-bar histogram."},"NPSSegmentMetricsResponse":{"properties":{"billing_state":{"type":"string","title":"Billing State"},"response_count":{"type":"integer","title":"Response Count"},"raw_mean":{"type":"number","title":"Raw Mean"},"nps_index":{"type":"number","title":"Nps Index"},"promoter_count":{"type":"integer","title":"Promoter Count"},"passive_count":{"type":"integer","title":"Passive Count"},"detractor_count":{"type":"integer","title":"Detractor Count"},"distribution":{"items":{"$ref":"#/components/schemas/NPSScoreCountResponse"},"type":"array","title":"Distribution"}},"type":"object","required":["billing_state","response_count","raw_mean","nps_index","promoter_count","passive_count","detractor_count","distribution"],"title":"NPSSegmentMetricsResponse","description":"Per-segment NPS metrics with both raw mean AND NPS Index.\n\nThe two are different metrics — never average across them. The\nbackoffice page surfaces both side by side per segment."},"NewFolderItem":{"properties":{"folder_id":{"type":"string","title":"Folder Id"},"name":{"type":"string","title":"Name"},"detected_at":{"type":"string","format":"date-time","title":"Detected At"}},"type":"object","required":["folder_id","name","detected_at"],"title":"NewFolderItem","description":"Single entry in the new-folders review list."},"NewFoldersResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"mode":{"type":"string","enum":["everything","selective"],"title":"Mode"},"new_folders":{"items":{"$ref":"#/components/schemas/NewFolderItem"},"type":"array","title":"New Folders"}},"type":"object","required":["connector_id","mode","new_folders"],"title":"NewFoldersResponse","description":"GET /v1/enterprise/connectors/{id}/new-folders response."},"NextEligibleResponse":{"properties":{"instrument":{"anyOf":[{"$ref":"#/components/schemas/SurveyInstrumentResponse"},{"type":"null"}]},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["reason"],"title":"NextEligibleResponse","description":"``GET /v1/surveys/next_eligible`` response.\n\n``instrument`` is ``None`` when no survey should be shown. The\nfrontend MUST treat ``None`` as \"render nothing\" — it never falls\nback to its own choice."},"NightlyAllowlistResponse":{"properties":{"allowed":{"items":{"type":"string"},"type":"array","title":"Allowed"},"destructive":{"items":{"type":"string"},"type":"array","title":"Destructive"}},"type":"object","required":["allowed","destructive"],"title":"NightlyAllowlistResponse","description":"Returned by ``GET /v1/admin/nightly/allowlist``.\n\nPowers the backoffice's \"Run now\" button — the frontend uses\n``destructive`` to decide whether to require the type-the-name\nconfirmation flow."},"NightlyJobRunResponse":{"properties":{"job_name":{"type":"string","title":"Job Name"},"scheduled_at":{"type":"string","format":"date-time","title":"Scheduled At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"status":{"type":"string","title":"Status"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["job_name","scheduled_at","status"],"title":"NightlyJobRunResponse","description":"Status of a single nightly job run."},"NightlyRunRowResponse":{"properties":{"job_name":{"type":"string","title":"Job Name"},"scheduled_at":{"type":"string","format":"date-time","title":"Scheduled At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"status":{"type":"string","title":"Status"},"duration_ms":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Duration Ms"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["job_name","scheduled_at","status"],"title":"NightlyRunRowResponse"},"NightlyRunsResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/NightlyRunRowResponse"},"type":"array","title":"Rows"},"window_hours":{"type":"integer","title":"Window Hours"},"failed_only":{"type":"boolean","title":"Failed Only"}},"type":"object","required":["rows","window_hours","failed_only"],"title":"NightlyRunsResponse"},"NotionAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"NotionAuthUrlResponse","description":"GET /v1/enterprise/connect/notion response."},"NotionPickerItem":{"properties":{"id":{"type":"string","title":"Id"},"object":{"type":"string","title":"Object"},"title":{"type":"string","title":"Title"},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Url"}},"type":"object","required":["id","object","title"],"title":"NotionPickerItem","description":"A page or database the integration can see.\n\n``object`` is ``\"page\"`` or ``\"database\"``. Notion IDs are\nglobally unique UUIDs across both types, but we surface the\nobject kind so the picker can render icons."},"NotionPickerResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/NotionPickerItem"},"type":"array","title":"Items"},"selected_scope":{"items":{"type":"string"},"type":"array","title":"Selected Scope"}},"type":"object","required":["items"],"title":"NotionPickerResponse","description":"GET /v1/enterprise/connectors/{id}/notion/items response.\n\nReturns every page + database the integration has access to.\nNotion requires the user to manually share each page/database\nwith the bot via the \"Add connections\" menu; anything not\nshared is invisible even with a valid access token."},"NotionScopeRequest":{"properties":{"scope_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Scope Ids"}},"type":"object","title":"NotionScopeRequest","description":"POST /v1/enterprise/connectors/{id}/notion/scope body.\n\nAdmin picks the set of Notion page/database ids to sync.\nEach entry is either a page UUID or a database UUID — the\nconnector auto-detects at sync time."},"NotionScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"scope_ids":{"items":{"type":"string"},"type":"array","title":"Scope Ids"},"scope_count":{"type":"integer","title":"Scope Count"}},"type":"object","required":["connector_id","scope_ids","scope_count"],"title":"NotionScopeResponse","description":"POST /v1/enterprise/connectors/{id}/notion/scope response.\n\nNo subscription counters — Notion does not support webhooks,\nso scope-setup only persists the scope."},"OAuthActionItemResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"connector_type":{"type":"string","title":"Connector Type"},"primary_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Identifier"},"connected_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connected By Email"},"bucket":{"type":"string","title":"Bucket"},"reason":{"type":"string","title":"Reason"}},"type":"object","required":["connector_id","tenant_id","tenant_name","connector_type","bucket","reason"],"title":"OAuthActionItemResponse"},"OAuthHealthResponse":{"properties":{"counts":{"additionalProperties":{"type":"integer"},"type":"object","title":"Counts"},"needs_customer_action":{"items":{"$ref":"#/components/schemas/OAuthActionItemResponse"},"type":"array","title":"Needs Customer Action"}},"type":"object","required":["counts","needs_customer_action"],"title":"OAuthHealthResponse"},"OffboardingEventListResponse":{"properties":{"events":{"items":{"$ref":"#/components/schemas/OffboardingEventSummary"},"type":"array","title":"Events"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["events","total"],"title":"OffboardingEventListResponse"},"OffboardingEventSummary":{"properties":{"id":{"type":"string","title":"Id"},"source":{"type":"string","enum":["webhook","manual"],"title":"Source"},"status":{"type":"string","enum":["received","processing","completed","failed","duplicate_ignored"],"title":"Status"},"target_email":{"type":"string","title":"Target Email"},"revoked_session_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Revoked Session Count"},"revoked_pat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Revoked Pat Count"},"deactivated_sa_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Deactivated Sa Count"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"received_at":{"type":"string","format":"date-time","title":"Received At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"actor_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Email"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"webhook_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Id"}},"type":"object","required":["id","source","status","target_email","received_at"],"title":"OffboardingEventSummary"},"OffboardingWebhookAck":{"properties":{"event_id":{"type":"string","title":"Event Id"},"status":{"type":"string","enum":["received","processing","completed","failed","duplicate_ignored"],"title":"Status"},"status_url":{"type":"string","title":"Status Url"}},"type":"object","required":["event_id","status","status_url"],"title":"OffboardingWebhookAck","description":"Response body for the webhook receiver."},"OrgHealthSchema":{"properties":{"composite_score":{"type":"number","title":"Composite Score"},"total_documents":{"type":"integer","title":"Total Documents"},"total_experts":{"type":"integer","title":"Total Experts"},"zero_coverage_domains":{"type":"integer","title":"Zero Coverage Domains"},"single_expert_domains":{"type":"integer","title":"Single Expert Domains"}},"type":"object","required":["composite_score","total_documents","total_experts","zero_coverage_domains","single_expert_domains"],"title":"OrgHealthSchema","description":"Organization-level health summary."},"OverrideCapRequest":{"properties":{"new_cap":{"type":"integer","maximum":10000.0,"minimum":0.0,"title":"New Cap"},"reason":{"type":"string","maxLength":200,"minLength":1,"title":"Reason"}},"type":"object","required":["new_cap","reason"],"title":"OverrideCapRequest"},"OverrideGetOut":{"properties":{"override":{"anyOf":[{"$ref":"#/components/schemas/OverrideOut"},{"type":"null"}]},"manifest":{"anyOf":[{"$ref":"#/components/schemas/ManifestEntryOut"},{"type":"null"}]}},"type":"object","title":"OverrideGetOut"},"OverrideHistoryOut":{"properties":{"items":{"items":{"$ref":"#/components/schemas/OverrideHistoryRowOut"},"type":"array","title":"Items"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["items","total"],"title":"OverrideHistoryOut"},"OverrideHistoryRowOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"template_id":{"type":"string","title":"Template Id"},"subject":{"type":"string","title":"Subject"},"preview_text":{"type":"string","title":"Preview Text"},"html":{"type":"string","title":"Html"},"edited_by":{"type":"string","title":"Edited By"},"edited_at":{"type":"string","format":"date-time","title":"Edited At"},"version":{"type":"integer","title":"Version"},"supersedes_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Supersedes Version"},"change_summary":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Change Summary"}},"type":"object","required":["id","template_id","subject","preview_text","html","edited_by","edited_at","version"],"title":"OverrideHistoryRowOut"},"OverrideOut":{"properties":{"template_id":{"type":"string","title":"Template Id"},"subject":{"type":"string","title":"Subject"},"preview_text":{"type":"string","title":"Preview Text"},"html":{"type":"string","title":"Html"},"edited_by":{"type":"string","title":"Edited By"},"edited_at":{"type":"string","format":"date-time","title":"Edited At"},"version":{"type":"integer","title":"Version"},"supersedes_version":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Supersedes Version"}},"type":"object","required":["template_id","subject","preview_text","html","edited_by","edited_at","version"],"title":"OverrideOut"},"OverrideSaveIn":{"properties":{"subject":{"type":"string","maxLength":2000,"minLength":1,"title":"Subject"},"preview_text":{"type":"string","maxLength":2000,"title":"Preview Text","default":""},"html":{"type":"string","minLength":1,"title":"Html"},"change_summary":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Change Summary"}},"type":"object","required":["subject","html"],"title":"OverrideSaveIn"},"OverviewResponse":{"properties":{"metrics":{"$ref":"#/components/schemas/DashboardMetricsResponse"},"quelvio_score":{"$ref":"#/components/schemas/QuelvioScoreResponse"},"nightly_jobs":{"items":{"$ref":"#/components/schemas/NightlyJobRunResponse"},"type":"array","title":"Nightly Jobs"}},"type":"object","required":["metrics","quelvio_score","nightly_jobs"],"title":"OverviewResponse","description":"Combined overview dashboard response."},"PMFAnswerCountResponse":{"properties":{"answer":{"type":"string","title":"Answer"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["answer","count"],"title":"PMFAnswerCountResponse","description":"One row in the PMF distribution view."},"PMFMetricsResponse":{"properties":{"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"overall":{"$ref":"#/components/schemas/PMFSegmentMetricsResponse"},"by_billing_state":{"items":{"$ref":"#/components/schemas/PMFSegmentMetricsResponse"},"type":"array","title":"By Billing State"},"response_rate":{"type":"number","title":"Response Rate"},"dismissal_count":{"type":"integer","title":"Dismissal Count"}},"type":"object","required":["window_start","window_end","overall","by_billing_state","response_rate","dismissal_count"],"title":"PMFMetricsResponse","description":"``GET /v1/admin/surveys/pmf/metrics`` response."},"PMFSegmentMetricsResponse":{"properties":{"billing_state":{"type":"string","title":"Billing State"},"response_count":{"type":"integer","title":"Response Count"},"very_disappointed_count":{"type":"integer","title":"Very Disappointed Count"},"pmf_index":{"type":"number","title":"Pmf Index"},"distribution":{"items":{"$ref":"#/components/schemas/PMFAnswerCountResponse"},"type":"array","title":"Distribution"}},"type":"object","required":["billing_state","response_count","very_disappointed_count","pmf_index","distribution"],"title":"PMFSegmentMetricsResponse","description":"Per-segment PMF metrics."},"PackAutoRenewRequest":{"properties":{"pack_type":{"type":"string","pattern":"^(fast|standard|deep)$","title":"Pack Type"},"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["pack_type","enabled"],"title":"PackAutoRenewRequest"},"PackAutoRenewResponse":{"properties":{"pack_type":{"type":"string","title":"Pack Type"},"auto_renew_enabled":{"type":"boolean","title":"Auto Renew Enabled"}},"type":"object","required":["pack_type","auto_renew_enabled"],"title":"PackAutoRenewResponse"},"PackInventoryResponse":{"properties":{"bundles":{"items":{"$ref":"#/components/schemas/BundleSchema"},"type":"array","title":"Bundles"},"auto_renew_settings":{"$ref":"#/components/schemas/AutoRenewSettings"}},"type":"object","required":["bundles","auto_renew_settings"],"title":"PackInventoryResponse"},"PackPurchaseRequest":{"properties":{"pack_type":{"type":"string","pattern":"^(fast|standard|deep)$","title":"Pack Type"},"quantity":{"type":"integer","maximum":10.0,"minimum":1.0,"title":"Quantity","default":1},"idempotency_key":{"type":"string","maxLength":255,"minLength":8,"title":"Idempotency Key"}},"type":"object","required":["pack_type","idempotency_key"],"title":"PackPurchaseRequest"},"PackPurchaseResponse":{"properties":{"bundles":{"items":{"$ref":"#/components/schemas/BundleSchema"},"type":"array","title":"Bundles"},"stripe_invoice_line_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Invoice Line Id"},"total_charged_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Total Charged Usd"},"idempotent_replay":{"type":"boolean","title":"Idempotent Replay","default":false}},"type":"object","required":["bundles","total_charged_usd"],"title":"PackPurchaseResponse"},"PackRefundSchema":{"properties":{"bundle_id":{"type":"integer","title":"Bundle Id"},"pack_type":{"type":"string","title":"Pack Type"},"queries_remaining":{"type":"integer","title":"Queries Remaining"},"queries_purchased":{"type":"integer","title":"Queries Purchased"},"credit_id":{"type":"string","title":"Credit Id"},"credit_amount_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Credit Amount Usd"}},"type":"object","required":["bundle_id","pack_type","queries_remaining","queries_purchased","credit_id","credit_amount_usd"],"title":"PackRefundSchema"},"PatSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id","description":"UUID of the PAT row."},"name":{"type":"string","title":"Name","description":"Human-readable label."},"key_prefix":{"type":"string","title":"Key Prefix","description":"First 16 chars of the plaintext key (``qlv_pat_<8 chars>``) — displayed in CLI / dashboard for identification.","examples":["qlv_pat_a1b2c3d4"]},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"scopes":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Scopes","description":"B-FE-11: scopes attached to this PAT, or NULL on legacy rows minted before the column existed. See ``ALLOWED_PAT_SCOPES`` for the closed set.","examples":[["READ","WRITE"]]}},"type":"object","required":["id","name","key_prefix","created_at"],"title":"PatSummary","description":"Sanitized PAT row — safe to return over HTTP. Never includes the\nfull plaintext key or the bcrypt hash."},"PatchPiecePermissionsRequest":{"properties":{"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails","description":"Operator-supplied permission_emails. Literal '__public_tenant__', a list of emails / '@domain' entries, or an empty list (when paired with ``op=remove``/``replace``, the action falls back to ``uploader_only`` semantics — manage-access Phase 1)."},"op":{"type":"string","enum":["replace","add","remove"],"title":"Op","description":"Mutation semantics. ``replace`` (default — legacy shape) overwrites the entire list with ``permission_emails``. ``add`` appends new entries to the existing list (de-duplicated). ``remove`` removes the entries in ``permission_emails`` from the existing list. Mirrors the bulk PATCH ``op`` semantics in ``permission_routes.py``.","default":"replace"},"permission_resolution_mode":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Permission Resolution Mode","description":"Advisory — every successful PATCH stamps 'manual_override' per design §3.13 unless the resulting list is empty, in which case the manage-access Phase 1 fallback stamps 'uploader_only'."},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At","description":"B+ Phase 3 design §3.13 — optional ISO 8601 timestamp at which the manual override auto-reverts. Must be timezone-aware and strictly in the future. ``None`` means an eternal override."}},"additionalProperties":false,"type":"object","required":["permission_emails"],"title":"PatchPiecePermissionsRequest","description":"``PATCH /v1/enterprise/library/{piece_id}/permissions`` body.\n\nReplaces the legacy raw ``request.json()`` parsing in\n``presentation/routes.py``. The route handler still catches the\nricher typed errors raised by\n:func:`validate_patch_permission_emails` (non-member email,\nunrecognised SSO domain, etc.) — the Pydantic layer only catches\nthe shape / sentinel-injection failures the application validator\nwould re-raise anyway.\n\nWire compatibility with the legacy raw-JSON parser:\n\n* Required: ``permission_emails`` — the literal sentinel or a\n  list[str] (may be empty when ``op`` is ``remove`` or ``replace``;\n  see the manage-access Phase 1 fallback below).\n* Optional: ``op`` ∈ {``replace`` | ``add`` | ``remove``} — mirrors\n  the bulk PATCH semantics from ``permission_routes.py``. Defaults\n  to ``replace`` for backward compatibility (the legacy whole-list\n  replace shape is unchanged).\n* Optional: ``permission_resolution_mode`` (advisory; the action\n  always stamps ``manual_override`` per design §3.13 unless the\n  resulting set is empty, in which case the manage-access Phase 1\n  fallback stamps ``uploader_only`` per the operator-feedback\n  contract — see ``apply_patch_piece_permissions``).\n\nNote: the legacy ``reason`` field was removed from this schema —\noperators no longer supply a justification on the wire. The\n``permission_audit_log.reason`` column remains on the model (legacy\nrows preserve their values) but new PATCH writes insert NULL. See\nthe v0.8 expand/contract migration plan for the eventual drop.\n\nManage-access Phase 1 — empty-list handling\n-------------------------------------------\nThe legacy contract rejected ``permission_emails=[]`` with 422\n(``permission_emails_empty``) forcing operators to \"delete and\nre-upload\" to reach an \"only me\" state. The redesign accepts an\nempty resulting list AFTER applying ``op``; the action then stamps\nthe row to ``uploader_only`` semantics\n(``permission_emails = [uploader_email]``,\n``permission_resolution_mode = 'uploader_only'``). The file stays\naccessible to its uploader rather than becoming invisible to\neveryone."},"PatchPiecePermissionsResponse":{"properties":{"piece_id":{"type":"string","title":"Piece Id","description":"Content piece UUID that was mutated."},"tenant_id":{"type":"string","title":"Tenant Id","description":"Tenant UUID the piece belongs to."},"permission_emails":{"items":{"type":"string"},"type":"array","title":"Permission Emails","description":"New ``permission_emails`` list after the PATCH. May contain the literal ``__public_tenant__`` sentinel (whole-tenant share)."},"permission_resolution_mode":{"type":"string","title":"Permission Resolution Mode","description":"Resolution mode after the PATCH. Typically ``manual_override`` per B+ design §3.13. Manage-access Phase 1: when the resulting grantee list is empty after applying ``op``, the action collapses to ``uploader_only`` semantics (the uploader's email as the sole grantee) rather than rejecting the request."},"permission_resolved_at":{"type":"string","title":"Permission Resolved At","description":"ISO-8601 timestamp of the resolution stamp (``now()`` at PATCH time)."},"permission_emails_before":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails Before","description":"Pre-PATCH ``permission_emails`` snapshot for the audit log. ``None`` if the piece had never been resolved before."},"resolution_mode_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Mode Before","description":"Pre-PATCH resolution mode (e.g. ``uploader_only``). ``None`` if the piece had never been resolved before."},"manual_override_expires_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Manual Override Expires At","description":"ISO-8601 timestamp at which this manual override auto-reverts (B+ Phase 3 design §3.13). ``None`` means an eternal override; the nightly ``revert_expired_manual_overrides`` cron only fires when this column is non-NULL and ``<= now()``."}},"type":"object","required":["piece_id","tenant_id","permission_emails","permission_resolution_mode","permission_resolved_at","permission_emails_before","resolution_mode_before"],"title":"PatchPiecePermissionsResponse","description":"PATCH /v1/enterprise/library/{piece_id}/permissions response (B+ Phase 3B).\n\nReturned to the dashboard after a successful operator-driven\n``permission_emails`` override. Mirrors the dataclass returned by\n:func:`apply_patch_piece_permissions` (see ``patch_piece_permissions.py``).\n\n``permission_emails_before`` and ``resolution_mode_before`` may\nlegitimately be ``None`` on a piece that's never been resolved\n(``permission_resolution_mode`` had not been stamped before the\nPATCH). All other fields are required."},"PaygCapOverrideRequest":{"properties":{"monthly_spend_cap_usd":{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$","title":"Monthly Spend Cap Usd"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"type":"object","required":["monthly_spend_cap_usd","reason"],"title":"PaygCapOverrideRequest"},"PaygConfigureRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"monthly_spend_cap_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$"},{"type":"null"}],"title":"Monthly Spend Cap Usd"}},"type":"object","required":["enabled"],"title":"PaygConfigureRequest"},"PaygConfigureResponse":{"properties":{"payg_enabled":{"type":"boolean","title":"Payg Enabled"},"monthly_spend_cap_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Monthly Spend Cap Usd"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"}},"type":"object","required":["payg_enabled","monthly_spend_cap_usd","effective_at"],"title":"PaygConfigureResponse"},"PaygHistoryResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/PaygQueryLogEntrySchema"},"type":"array","title":"Items"}},"type":"object","required":["items"],"title":"PaygHistoryResponse"},"PaygQueryLogEntrySchema":{"properties":{"id":{"type":"integer","title":"Id"},"user_id":{"type":"string","title":"User Id"},"query_type":{"type":"string","title":"Query Type"},"rate_charged_usd":{"type":"string","title":"Rate Charged Usd"},"charged_at":{"type":"string","format":"date-time","title":"Charged At"},"stripe_usage_record_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe Usage Record Id"},"aggregated_to_stripe_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Aggregated To Stripe At"}},"type":"object","required":["id","user_id","query_type","rate_charged_usd","charged_at"],"title":"PaygQueryLogEntrySchema"},"PaygStatusResponse":{"properties":{"enabled":{"type":"boolean","title":"Enabled"},"monthly_spend_cap_usd":{"type":"string","title":"Monthly Spend Cap Usd"},"current_period_spent_usd":{"type":"string","title":"Current Period Spent Usd"},"spent_percent":{"type":"string","title":"Spent Percent"},"period_start":{"type":"string","format":"date-time","title":"Period Start"},"period_end":{"type":"string","format":"date-time","title":"Period End"},"days_until_reset":{"type":"integer","title":"Days Until Reset"},"threshold_alerts":{"$ref":"#/components/schemas/PaygThresholdAlerts"}},"type":"object","required":["enabled","monthly_spend_cap_usd","current_period_spent_usd","spent_percent","period_start","period_end","days_until_reset","threshold_alerts"],"title":"PaygStatusResponse"},"PaygThresholdAlerts":{"properties":{"50_pct_sent":{"type":"boolean","title":"50 Pct Sent","default":false},"80_pct_sent":{"type":"boolean","title":"80 Pct Sent","default":false},"100_pct_sent":{"type":"boolean","title":"100 Pct Sent","default":false}},"type":"object","title":"PaygThresholdAlerts","description":"Three boolean flags matching the Lock ``PaygStatusResponse.threshold_alerts``\nshape. ``True`` means the email/alert fired this period."},"PaymentStatusResponse":{"properties":{"has_payment_method":{"type":"boolean","title":"Has Payment Method"},"last4":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last4"},"brand":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Brand"}},"type":"object","required":["has_payment_method"],"title":"PaymentStatusResponse","description":"GET /v1/enterprise/billing/payment-status response."},"PendingCelebration":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"kind":{"type":"string","enum":["referee_welcome","referrer_conversion"],"title":"Kind"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"gb_amount":{"type":"integer","title":"Gb Amount"},"referrer_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referrer Name"},"referrer_email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Referrer Email"},"referee_email":{"anyOf":[{"type":"string","format":"email"},{"type":"null"}],"title":"Referee Email"},"referee_tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Referee Tenant Name"}},"type":"object","required":["id","kind","created_at","gb_amount","referrer_name","referrer_email","referee_email","referee_tenant_name"],"title":"PendingCelebration","description":"One unseen celebration the current user should be shown.\n\nField discriminator is ``kind``:\n  * ``referee_welcome`` — referrer_* fields populated, referee_* null\n  * ``referrer_conversion`` — referee_* fields populated, referrer_* null"},"PeopleIntelligenceResponse":{"properties":{"knowledge_health":{"type":"number","title":"Knowledge Health"},"single_expert_risks":{"type":"integer","title":"Single Expert Risks"},"experts_discovered":{"type":"integer","title":"Experts Discovered"},"experts":{"items":{"$ref":"#/components/schemas/ExpertProfileSchema"},"type":"array","title":"Experts"}},"type":"object","required":["knowledge_health","single_expert_risks","experts_discovered"],"title":"PeopleIntelligenceResponse","description":"GET /v1/enterprise/intelligence/people response.\n\nLive (not precomputed by nightly cron) so the dashboard reflects current\nstate immediately after ingestion."},"PerKeyResponse":{"properties":{"api_key_id":{"type":"string","format":"uuid","title":"Api Key Id"},"auth_source":{"type":"string","title":"Auth Source"},"key_prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Prefix"},"key_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Key Label"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"},"member_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Member Id"},"member_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Email"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"window_hours":{"type":"integer","title":"Window Hours"},"since":{"type":"string","format":"date-time","title":"Since"},"first_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Seen"},"last_seen":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Seen"},"query_count":{"type":"integer","title":"Query Count"},"error_count":{"type":"integer","title":"Error Count"},"error_rate":{"type":"number","title":"Error Rate"},"signals":{"items":{"$ref":"#/components/schemas/SuspiciousSignalSchema"},"type":"array","title":"Signals"},"off_hours_heatmap":{"items":{"type":"integer"},"type":"array","title":"Off Hours Heatmap","description":"Per-UTC-hour query count, length 24. Index i = queries with created_at.hour == i. All zeros when no activity in window."},"bucket_meaning":{"type":"string","const":"hour_of_day_utc","title":"Bucket Meaning","description":"Heatmap shape discriminator. Frontend selects rendering off this field, never off heatmap length.","default":"hour_of_day_utc"}},"type":"object","required":["api_key_id","auth_source","key_prefix","key_label","tenant_id","tenant_name","member_id","member_email","created_at","revoked_at","last_used_at","window_hours","since","first_seen","last_seen","query_count","error_count","error_rate","signals"],"title":"PerKeyResponse"},"PermissionAuditLogEntryResponse":{"properties":{"id":{"type":"string","title":"Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"piece_id":{"type":"string","title":"Piece Id"},"mutation_source":{"type":"string","title":"Mutation Source"},"mutated_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mutated By Email"},"mutated_by_user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mutated By User Id"},"permission_emails_before":{"anyOf":[{"items":{},"type":"array"},{"type":"null"}],"title":"Permission Emails Before"},"permission_emails_after":{"items":{},"type":"array","title":"Permission Emails After"},"resolution_mode_before":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Resolution Mode Before"},"resolution_mode_after":{"type":"string","title":"Resolution Mode After"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"mutated_at":{"type":"string","format":"date-time","title":"Mutated At"}},"type":"object","required":["id","tenant_id","piece_id","mutation_source","permission_emails_after","resolution_mode_after","mutated_at"],"title":"PermissionAuditLogEntryResponse","description":"One ``permission_audit_log`` row in dashboard render shape."},"PermissionAuditTailResponse":{"properties":{"entries":{"items":{"$ref":"#/components/schemas/PermissionAuditLogEntryResponse"},"type":"array","title":"Entries"},"limit":{"type":"integer","title":"Limit"},"since":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Since"}},"type":"object","required":["entries","limit"],"title":"PermissionAuditTailResponse","description":"Audit-tail response — ``entries`` ordered ``mutated_at DESC``."},"PermissionBackfillConnectorResponse":{"properties":{"connector_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector Id"},"connector_type":{"type":"string","title":"Connector Type"},"pieces_total":{"type":"integer","title":"Pieces Total"},"pieces_backfilled":{"type":"integer","title":"Pieces Backfilled"},"pieces_pending":{"type":"integer","title":"Pieces Pending"}},"type":"object","required":["connector_type","pieces_total","pieces_backfilled","pieces_pending"],"title":"PermissionBackfillConnectorResponse","description":"Per-connector roll-up for one tenant.\n\n``connector_id`` is NULL for ``connector_type='upload'`` — uploads\ndon't have an ``enterprise_connectors`` row."},"PermissionBackfillSummaryResponse":{"properties":{"total_tenants":{"type":"integer","title":"Total Tenants"},"completed":{"type":"integer","title":"Completed"},"errored":{"type":"integer","title":"Errored"},"killed":{"type":"integer","title":"Killed"},"running":{"type":"integer","title":"Running"},"pieces_processed_total":{"type":"integer","title":"Pieces Processed Total"},"last_advanced_max":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Advanced Max"},"tenants":{"items":{"$ref":"#/components/schemas/BackfillTenantSummaryItem"},"type":"array","title":"Tenants","default":[]}},"type":"object","required":["total_tenants","completed","errored","killed","running","pieces_processed_total"],"title":"PermissionBackfillSummaryResponse","description":"Aggregated counts across every Phase 4 progress row.\n\nShape matches the dashboard \"headline cards\" — one number per\nstatus bucket plus the running total of pieces processed and the\nmost-recent forward step across all rows.\n\n``tenants`` carries the top 100 rows ordered by\n``last_advanced_at DESC`` so the dashboard can render a per-tenant\nstrip in one round trip. The full long tail is reachable via\n``GET /v1/admin/permission-backfill/tenant/{tenant_id}``."},"PermissionBackfillTenantDetailResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"pieces_processed":{"type":"integer","title":"Pieces Processed"},"pieces_unresolved":{"type":"integer","title":"Pieces Unresolved"},"pieces_qdrant_updated":{"type":"integer","title":"Pieces Qdrant Updated"},"last_processed_piece_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Processed Piece Id"},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error"},"last_error_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Error At"},"connectors":{"items":{"$ref":"#/components/schemas/PermissionBackfillConnectorResponse"},"type":"array","title":"Connectors"},"recent_audit":{"items":{"$ref":"#/components/schemas/PermissionAuditLogEntryResponse"},"type":"array","title":"Recent Audit","default":[]}},"type":"object","required":["tenant_id","tenant_name","started_at","pieces_processed","pieces_unresolved","pieces_qdrant_updated","connectors"],"title":"PermissionBackfillTenantDetailResponse","description":"One tenant's progress row + per-connector breakdown.\n\nThe ``recent_audit`` inline slice (B-FE-2) carries the top 20\n``permission_audit_log`` rows for the tenant ordered by\n``mutated_at DESC``. Backoffice PR #31 reads it to avoid a second\nround-trip to ``GET /v1/admin/permission-backfill/audit-tail`` when\nrendering the per-tenant drilldown — the audit-tail endpoint is\nstill available for cross-tenant scroll-back and larger windows."},"PermissionErrorResponse":{"properties":{"error_code":{"type":"string","title":"Error Code","description":"Machine-parsable reason — the canonical key the frontend uses to render inline errors and toasts."},"message":{"type":"string","title":"Message","description":"Operator-readable copy. Safe to render in a toast or inline."},"details":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Details","description":"Optional structured context (limit_kind, current_tier, affected_count, ...). Populated when the endpoint has additional shape to surface (e.g. tier-gate failures, cap-exceeded counts)."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error","description":"DEPRECATED — mirrors ``error_code`` for the FS-contracts deprecation cycle. New consumers MUST switch to ``error_code``. Slated for removal in the next breaking-change train.","deprecated":true}},"additionalProperties":false,"type":"object","required":["error_code","message"],"title":"PermissionErrorResponse","description":"Canonical error body for the three permission-PATCH endpoints.\n\nAll 4xx responses from Phase 3A / 3B / 3C return this shape so the\nfrontend client renders one error type. ``error_code`` is the\nmachine-parsable key (frontend switches on it for inline UI), and\n``message`` is the operator-readable copy.\n\n``error`` is the deprecated alias of ``error_code`` retained for the\nFS-contracts migration cycle (Phase 3C used to emit only ``error``).\nThe :func:`build_permission_error_detail` helper populates both keys\nuntil the deprecation cycle closes."},"PermissionRolloutStatusResponse":{"properties":{"phase":{"type":"string","enum":["not_started","backfill_in_progress","backfill_complete","pre_flip","post_flip"],"title":"Phase","description":"Coarse rollout state for the caller's tenant. The FE banner renders one branch per value."},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message","description":"Optional human-readable string. The FE falls back to its own per-phase copy when this is None."},"progress_pct":{"anyOf":[{"type":"number","maximum":100.0,"minimum":0.0},{"type":"null"}],"title":"Progress Pct","description":"Backfill progress as a percent (0–100). Only populated when ``phase == 'backfill_in_progress'``. NULL otherwise."},"flip_date":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flip Date","description":"ISO-8601 date string for the scheduled Phase 5 flip. Only populated when ``phase == 'pre_flip'``. NULL otherwise."}},"additionalProperties":false,"type":"object","required":["phase"],"title":"PermissionRolloutStatusResponse","description":"``GET /v1/enterprise/permission-rollout-status`` 200 response.\n\nFields are intentionally optional so the FE can branch on\n``phase`` alone — see the per-phase invariants in the module\ndocstring above."},"PersonaScoreResponse":{"properties":{"persona":{"type":"string","title":"Persona"},"composite":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Composite"},"nps_avg":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Nps Avg"},"pmf_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Pmf Pct"},"csat_avg":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Csat Avg"},"ces_avg":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ces Avg"},"response_count":{"type":"integer","title":"Response Count"}},"type":"object","required":["persona","response_count"],"title":"PersonaScoreResponse","description":"Single persona's Quelvio Score."},"PersonalReferralCode":{"properties":{"code":{"type":"string","title":"Code"},"share_url":{"type":"string","title":"Share Url"}},"type":"object","required":["code","share_url"],"title":"PersonalReferralCode"},"PlanChangeCorpusOutcome":{"properties":{"new_corpus_tier_option_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"New Corpus Tier Option Id"},"new_total_gb":{"type":"integer","title":"New Total Gb","default":0},"new_additional_monthly_cost_usd":{"type":"string","title":"New Additional Monthly Cost Usd","default":"0.00"},"stripe_operation":{"type":"string","title":"Stripe Operation","default":"no_change"},"stripe_link_pending":{"type":"boolean","title":"Stripe Link Pending","default":false}},"type":"object","title":"PlanChangeCorpusOutcome"},"PlanChangeRequest":{"properties":{"target_plan_tier":{"type":"string","pattern":"^(free|team|growth|enterprise)$","title":"Target Plan Tier"},"target_seat_count":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Target Seat Count"},"target_corpus_tier_option_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Target Corpus Tier Option Id"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","required":["target_plan_tier"],"title":"PlanChangeRequest","description":"Request body for POST /api/billing/plan/change."},"PlanChangeResponse":{"properties":{"status":{"type":"string","title":"Status"},"from_plan_tier":{"type":"string","title":"From Plan Tier"},"to_plan_tier":{"type":"string","title":"To Plan Tier"},"from_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"From Seat Count"},"to_seat_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"To Seat Count"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"},"proration_applied_usd":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Proration Applied Usd"},"scheduled_change_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scheduled Change Id"},"flapping_active":{"type":"boolean","title":"Flapping Active","default":false},"audit_log_id":{"type":"integer","title":"Audit Log Id"},"pack_refund_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Pack Refund Usd","default":"0.00"},"pack_refund_credit_ids":{"items":{"type":"string"},"type":"array","title":"Pack Refund Credit Ids"},"pool_reset":{"type":"boolean","title":"Pool Reset","default":false},"payg_closed":{"type":"boolean","title":"Payg Closed","default":false},"corpus_outcome":{"$ref":"#/components/schemas/PlanChangeCorpusOutcome"}},"type":"object","required":["status","from_plan_tier","to_plan_tier","effective_at","audit_log_id"],"title":"PlanChangeResponse","description":"Response shape for plan / seat change endpoints (and cron-applied)."},"PlanEntryView":{"properties":{"tier":{"type":"string","enum":["free","team","growth","enterprise"],"title":"Tier"},"seat_price_per_month_intl_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Seat Price Per Month Intl Usd","description":"Per-seat list price for non-EU/US regions. None for free tier and enterprise."},"seat_price_per_month_eu_us_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Seat Price Per Month Eu Us Usd","description":"Per-seat list price for EU + US regions. None for free tier; enterprise = 'contract'."},"daily_kt_pool":{"type":"integer","title":"Daily Kt Pool","description":"Daily kT entitlement per seat for this tier."},"default_corpus_gb":{"type":"number","title":"Default Corpus Gb","description":"Default included corpus GB for this tier."},"corpus_upgrade_ladder":{"items":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"}]},"type":"object"},"type":"array","title":"Corpus Upgrade Ladder","description":"Optional corpus upgrade tiers (Tier 1..5) with per-tier prices. Free + Enterprise = empty."},"member_cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Member Cap","description":"Max seats on this tier. None = unlimited (enterprise)."},"api_rpm_cap":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Api Rpm Cap","description":"API rate limit (requests per minute). None = unlimited."},"audit_retention_days":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Audit Retention Days","description":"Audit log retention in days. None = enterprise (configurable)."},"annual_discount_pct":{"type":"number","title":"Annual Discount Pct","description":"Annual prepay discount (0.15 = 15% off monthly list).","default":0.15}},"type":"object","required":["tier","daily_kt_pool","default_corpus_gb"],"title":"PlanEntryView","description":"Same shape as ``PlanEntry``. Plans don't carry ``verified_at`` —\ntheir values mirror the DB seed and are tracked via the migration's\n``revision`` field, not a per-entry timestamp. No ``staleness_days``.\n\nDeclared as a subclass (rather than reusing ``PlanEntry`` directly\nin ``CatalogResponse``) so future ``CatalogResponse.plans`` field\nadditions land on the view, not the catalog primitive."},"PlanPricingSchema":{"properties":{"tier":{"type":"string","title":"Tier"},"display_name":{"type":"string","title":"Display Name"},"tagline":{"type":"string","title":"Tagline"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"base_monthly_per_seat_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Base Monthly Per Seat Usd"},"monthly_per_seat_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Monthly Per Seat Usd"},"annual_per_seat_per_month_usd":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Annual Per Seat Per Month Usd"},"annual_discount_percent":{"type":"number","title":"Annual Discount Percent"},"is_recommended":{"type":"boolean","title":"Is Recommended"},"is_self_serve":{"type":"boolean","title":"Is Self Serve"},"max_users":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Users"},"highlights":{"items":{"type":"string"},"type":"array","title":"Highlights"},"corpus_options":{"items":{"$ref":"#/components/schemas/CorpusOptionSchema"},"type":"array","title":"Corpus Options","description":"Available corpus add-on tiers (GB) for this plan in the tenant's region. Empty for Free / Enterprise. The modal renders these as an inline picker next to the seat stepper."}},"type":"object","required":["tier","display_name","tagline","description","base_monthly_per_seat_usd","monthly_per_seat_usd","annual_per_seat_per_month_usd","annual_discount_percent","is_recommended","is_self_serve","max_users","highlights"],"title":"PlanPricingSchema","description":"One plan tier with region-adjusted pricing."},"PresignPartResponse":{"properties":{"part_number":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Part Number"},"presigned_url":{"type":"string","title":"Presigned Url"}},"type":"object","required":["part_number","presigned_url"],"title":"PresignPartResponse","description":"One part's upload URL in the presign response."},"PresignUploadRequest":{"properties":{"filename":{"type":"string","maxLength":500,"minLength":1,"title":"Filename"},"size_bytes":{"type":"integer","maximum":5368709120.0,"minimum":5242880.0,"title":"Size Bytes"},"content_type":{"type":"string","maxLength":100,"minLength":1,"title":"Content Type"},"file_hash":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"File Hash","description":"Optional content-identity hash for dedup."},"title":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Title"},"author_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Name"},"author_email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Email"},"department":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Department"},"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails"}},"type":"object","required":["filename","size_bytes","content_type"],"title":"PresignUploadRequest","description":"Request body for the presign endpoint.\n\n``file_hash`` is the frontend-computed identity key (hash of first\n1MB + last 1MB + size hex). Sending it lets the backend dedup\nre-uploads of the same file by the same tenant — the caller gets\nback the existing piece_id with no upload work performed."},"PresignUploadResponse":{"properties":{"deduplicated":{"type":"boolean","title":"Deduplicated"},"existing_piece_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Piece Id"},"upload_id":{"type":"string","title":"Upload Id"},"s3_key":{"type":"string","title":"S3 Key"},"content_piece_id":{"type":"string","title":"Content Piece Id"},"part_size_bytes":{"type":"integer","title":"Part Size Bytes"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"parts":{"items":{"$ref":"#/components/schemas/PresignPartResponse"},"type":"array","title":"Parts"}},"type":"object","required":["deduplicated","existing_piece_id","upload_id","s3_key","content_piece_id","part_size_bytes","expires_at","parts"],"title":"PresignUploadResponse","description":"Response body for the presign endpoint.\n\nWhen ``deduplicated == true``, the file already exists for this\ntenant. The caller should treat ``existing_piece_id`` as the\nfinal piece id and skip the rest of the upload flow."},"PresignedUrlRequest":{"properties":{"filename":{"type":"string","maxLength":500,"minLength":1,"title":"Filename"},"size_bytes":{"type":"integer","maximum":5368709120.0,"minimum":1.0,"title":"Size Bytes"},"content_type":{"type":"string","maxLength":100,"minLength":1,"title":"Content Type","description":"MIME type, e.g. application/pdf"},"title":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Title","description":"Document title. Defaults to filename if omitted."},"author_name":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Name"},"author_email":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Author Email"},"department":{"anyOf":[{"type":"string","maxLength":255},{"type":"null"}],"title":"Department"},"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails"}},"type":"object","required":["filename","size_bytes","content_type"],"title":"PresignedUrlRequest","description":"POST /v1/enterprise/content/upload-url request body."},"PresignedUrlResponse":{"properties":{"upload_url":{"type":"string","title":"Upload Url","description":"Presigned S3 PUT URL (15-min expiry)"},"s3_key":{"type":"string","title":"S3 Key"},"content_piece_id":{"type":"string","title":"Content Piece Id"}},"type":"object","required":["upload_url","s3_key","content_piece_id"],"title":"PresignedUrlResponse","description":"POST /v1/enterprise/content/upload-url response."},"PreviewIn":{"properties":{"subject":{"type":"string","maxLength":2000,"minLength":1,"title":"Subject"},"preview_text":{"type":"string","maxLength":2000,"title":"Preview Text","default":""},"html":{"type":"string","minLength":1,"title":"Html"},"sample_context":{"type":"object","title":"Sample Context","description":"Token values for the render. Missing required tokens default to a sentinel placeholder ('<sample:{token}>') so operators see where each value lands without having to populate the full context manually."}},"type":"object","required":["subject","html"],"title":"PreviewIn"},"PreviewLineItem":{"properties":{"description":{"type":"string","title":"Description"},"amount_usd":{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$","title":"Amount Usd"},"proration":{"type":"boolean","title":"Proration"},"quantity":{"type":"integer","title":"Quantity"},"price_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Price Id"}},"type":"object","required":["description","amount_usd","proration","quantity"],"title":"PreviewLineItem"},"PreviewOut":{"properties":{"subject":{"type":"string","title":"Subject"},"preview_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Preview Text"},"html":{"type":"string","title":"Html"},"tokens_applied":{"items":{"type":"string"},"type":"array","title":"Tokens Applied"},"tokens_defaulted":{"items":{"type":"string"},"type":"array","title":"Tokens Defaulted"}},"type":"object","required":["subject","html","tokens_applied","tokens_defaulted"],"title":"PreviewOut"},"Pricing":{"properties":{"formula":{"type":"string","title":"Formula","description":"Human-readable formula. Lints in CI; rendered in the backoffice catalog viewer."},"unit":{"type":"string","enum":["USD_per_call","USD_per_mtok","USD_per_GB_month","USD_per_GB","USD_per_request","USD_per_minute","USD_per_hour","USD_per_seat_month","USD_per_event","USD_per_secret_month","USD_per_month","USD_per_transaction","percent_of_revenue","free","contract_negotiated"],"title":"Unit"},"rates":{"additionalProperties":{"anyOf":[{"type":"number"},{"type":"string"}]},"type":"object","title":"Rates","description":"Named rate constants the formula references. Strings allowed for 'contract_negotiated' / 'see provider page'."}},"type":"object","required":["formula","unit"],"title":"Pricing","description":"Common pricing envelope.\n\n``formula`` is human-readable documentation of how the cost is computed.\n``unit`` declares the canonical billing unit. ``rates`` is a free-form\ndict of named rate constants the formula refers to (e.g. ``input_per_mtok``,\n``output_per_mtok``, ``per_gb_month``). Free-form because rate vocabulary\nvaries across providers (Anthropic uses input/output split, Voyage uses\na single per-token rate, S3 uses storage + request rates)."},"ProjectionBreakdown":{"properties":{"ingestion":{"type":"integer","title":"Ingestion"},"index":{"type":"integer","title":"Index"},"retrieval":{"type":"integer","title":"Retrieval"}},"type":"object","required":["ingestion","index","retrieval"],"title":"ProjectionBreakdown","description":"Projected monthly token consumption per dimension.\n\nv0.8 dimension names (renamed from v0.5 ``created`` / ``maintained`` /\n``retrieved`` — see CLAUDE.md §\"Billing (v0.8 Phase A)\")."},"ProjectionResponse":{"properties":{"projected_tokens":{"type":"integer","title":"Projected Tokens"},"projected_cost_cents":{"type":"integer","title":"Projected Cost Cents"},"breakdown":{"$ref":"#/components/schemas/ProjectionBreakdown"},"days_in_period":{"type":"integer","title":"Days In Period"},"days_elapsed":{"type":"integer","title":"Days Elapsed"}},"type":"object","required":["projected_tokens","projected_cost_cents","breakdown","days_in_period","days_elapsed"],"title":"ProjectionResponse","description":"GET /v1/enterprise/billing/projection response."},"PromptVariantResponse":{"properties":{"variant_key":{"type":"string","title":"Variant Key"},"label":{"type":"string","title":"Label"},"regions":{"items":{"type":"string"},"type":"array","title":"Regions"},"provider":{"type":"string","title":"Provider"},"model":{"type":"string","title":"Model"},"fallback":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fallback"},"prompt_filename":{"type":"string","title":"Prompt Filename"},"prompt_text":{"type":"string","title":"Prompt Text"},"prompt_token_estimate":{"type":"integer","title":"Prompt Token Estimate"}},"type":"object","required":["variant_key","label","regions","provider","model","prompt_filename","prompt_text","prompt_token_estimate"],"title":"PromptVariantResponse"},"ProvisionEnterpriseRequest":{"properties":{"seat_count":{"type":"integer","exclusiveMinimum":0.0,"title":"Seat Count"},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"},"override_base_price_per_user_per_period_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{1,2})?$"},{"type":"null"}],"title":"Override Base Price Per User Per Period Usd"},"override_daily_kt_pool_per_seat":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Override Daily Kt Pool Per Seat"}},"additionalProperties":false,"type":"object","required":["seat_count","reason"],"title":"ProvisionEnterpriseRequest","description":"Lock-additive — see dispatch-log OCI-5 (F-16)."},"ProvisionStripeCatalogRequest":{"properties":{"is_live":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Live","description":"Stripe environment to provision against. ``None`` (default) uses whatever ``settings.stripe_mode`` resolves to in the current ECS task — typically 'sandbox' on dev, 'live' on prod. Override to force the other environment when operator-side env vars + Stripe SDK key already match the intended target. Passing the wrong value when Stripe SDK is configured for a different mode will create Prices in the wrong environment — match this flag to your STRIPE_MODE / STRIPE_SECRET_KEY / STRIPE_LIVE_SECRET_KEY set."}},"type":"object","title":"ProvisionStripeCatalogRequest","description":"Body for POST /admin/billing/provision-stripe-catalog."},"ProvisionStripeCatalogResponse":{"properties":{"is_live":{"type":"boolean","title":"Is Live"},"seat_rows":{"type":"integer","title":"Seat Rows"},"corpus_rows":{"type":"integer","title":"Corpus Rows"},"pack_rows":{"type":"integer","title":"Pack Rows"},"payg_rows":{"type":"integer","title":"Payg Rows"},"kt_rows":{"type":"integer","title":"Kt Rows"},"plans_skipped_non_self_serve":{"type":"integer","title":"Plans Skipped Non Self Serve"},"plans_skipped_free":{"type":"integer","title":"Plans Skipped Free"},"duration_ms":{"type":"integer","title":"Duration Ms"}},"type":"object","required":["is_live","seat_rows","corpus_rows","pack_rows","payg_rows","kt_rows","plans_skipped_non_self_serve","plans_skipped_free","duration_ms"],"title":"ProvisionStripeCatalogResponse","description":"Response from POST /admin/billing/provision-stripe-catalog."},"PurgeResponse":{"properties":{"user_memory_rows_reset":{"type":"integer","title":"User Memory Rows Reset"},"user_event_significance_rows_deleted":{"type":"integer","title":"User Event Significance Rows Deleted"},"memory_edit_audit_log_rows_deleted":{"type":"integer","title":"Memory Edit Audit Log Rows Deleted"}},"type":"object","required":["user_memory_rows_reset","user_event_significance_rows_deleted","memory_edit_audit_log_rows_deleted"],"title":"PurgeResponse"},"QualityGateOverviewResponse":{"properties":{"recent_rejections":{"items":{"$ref":"#/components/schemas/_RejectionRow"},"type":"array","title":"Recent Rejections"},"redaction_summary":{"additionalProperties":{"type":"integer"},"type":"object","title":"Redaction Summary"},"override_rate":{"type":"object","title":"Override Rate"}},"type":"object","required":["recent_rejections","redaction_summary","override_rate"],"title":"QualityGateOverviewResponse"},"QuelvioScoreResponse":{"properties":{"enterprise":{"$ref":"#/components/schemas/PersonaScoreResponse"}},"type":"object","required":["enterprise"],"title":"QuelvioScoreResponse","description":"Quelvio Score — enterprise-only at v0.9.4.\n\nv0.9.4 retired the creator + developer personas; only the\nenterprise persona is tracked going forward. Historical survey\nrows tagged with the retired personas remain in the database but\nare not surfaced. The schema keeps the wrapper object (rather\nthan inlining the single PersonaScoreResponse) so a future\ncohort addition doesn't churn the API shape."},"QueryDrillDownResponse":{"properties":{"query_log_id":{"type":"string","format":"uuid","title":"Query Log Id"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"coverage_level":{"type":"string","title":"Coverage Level"},"mode":{"type":"string","title":"Mode"},"status":{"type":"string","title":"Status"},"retrieval_execution_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retrieval Execution State"},"query_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Type"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"calls":{"items":{"$ref":"#/components/schemas/LlmCallResponse"},"type":"array","title":"Calls"},"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"total_input_units":{"type":"integer","title":"Total Input Units"},"total_output_units":{"type":"integer","title":"Total Output Units"},"sources_returned":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sources Returned"},"input_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Input Tokens"},"output_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Output Tokens"}},"type":"object","required":["query_log_id","coverage_level","mode","status","created_at","calls","total_cost_usd_micros","total_input_units","total_output_units"],"title":"QueryDrillDownResponse"},"QueryListItemResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"mode":{"type":"string","title":"Mode"},"status":{"type":"string","title":"Status"},"coverage_level":{"type":"string","title":"Coverage Level"},"sources_returned":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sources Returned"},"input_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Input Tokens"},"output_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Output Tokens"},"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"cost_expansion_usd_micros":{"type":"integer","title":"Cost Expansion Usd Micros"},"cost_classifier_usd_micros":{"type":"integer","title":"Cost Classifier Usd Micros"},"cost_rewriting_usd_micros":{"type":"integer","title":"Cost Rewriting Usd Micros"},"cost_embedding_usd_micros":{"type":"integer","title":"Cost Embedding Usd Micros"},"cost_synthesis_usd_micros":{"type":"integer","title":"Cost Synthesis Usd Micros"},"llm_call_count":{"type":"integer","title":"Llm Call Count"}},"type":"object","required":["id","tenant_id","tenant_name","occurred_at","mode","status","coverage_level","sources_returned","input_tokens","output_tokens","total_cost_usd_micros","cost_expansion_usd_micros","cost_classifier_usd_micros","cost_rewriting_usd_micros","cost_embedding_usd_micros","cost_synthesis_usd_micros","llm_call_count"],"title":"QueryListItemResponse"},"QueryListResponse":{"properties":{"total":{"type":"integer","title":"Total","description":"Total matching rows (across pages)"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"items":{"items":{"$ref":"#/components/schemas/QueryListItemResponse"},"type":"array","title":"Items"},"window_totals":{"$ref":"#/components/schemas/QueryListWindowTotalsResponse"}},"type":"object","required":["total","limit","offset","items","window_totals"],"title":"QueryListResponse"},"QueryListWindowTotalsResponse":{"properties":{"total_cost_usd_micros":{"type":"integer","title":"Total Cost Usd Micros"},"cost_expansion_usd_micros":{"type":"integer","title":"Cost Expansion Usd Micros"},"cost_classifier_usd_micros":{"type":"integer","title":"Cost Classifier Usd Micros"},"cost_rewriting_usd_micros":{"type":"integer","title":"Cost Rewriting Usd Micros"},"cost_embedding_usd_micros":{"type":"integer","title":"Cost Embedding Usd Micros"},"cost_synthesis_usd_micros":{"type":"integer","title":"Cost Synthesis Usd Micros"},"llm_call_count":{"type":"integer","title":"Llm Call Count"},"query_count":{"type":"integer","title":"Query Count"}},"type":"object","required":["total_cost_usd_micros","cost_expansion_usd_micros","cost_classifier_usd_micros","cost_rewriting_usd_micros","cost_embedding_usd_micros","cost_synthesis_usd_micros","llm_call_count","query_count"],"title":"QueryListWindowTotalsResponse","description":"Sum across the entire filtered window (not just the current page)."},"QueueDepthResponse":{"properties":{"queues":{"items":{"$ref":"#/components/schemas/QueueDepthRowResponse"},"type":"array","title":"Queues"}},"type":"object","required":["queues"],"title":"QueueDepthResponse"},"QueueDepthRowResponse":{"properties":{"name":{"type":"string","title":"Name"},"visible_messages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Visible Messages"},"dlq_messages":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Dlq Messages"},"visible_degraded_threshold":{"type":"integer","title":"Visible Degraded Threshold"},"visible_fail_threshold":{"type":"integer","title":"Visible Fail Threshold"},"dlq_degraded_threshold":{"type":"integer","title":"Dlq Degraded Threshold"},"dlq_fail_threshold":{"type":"integer","title":"Dlq Fail Threshold"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"}},"type":"object","required":["name","visible_degraded_threshold","visible_fail_threshold","dlq_degraded_threshold","dlq_fail_threshold"],"title":"QueueDepthRowResponse"},"RankingConfigOverrideRequest":{"properties":{"status":{"anyOf":[{"type":"string","pattern":"^(disabled|shadow|enabled)$"},{"type":"null"}],"title":"Status","description":"Force this lifecycle state. When set, manually_overridden=true."},"clear_override":{"type":"boolean","title":"Clear Override","description":"When True, set manually_overridden=false; evaluator resumes.","default":false},"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason","description":"Operator-supplied reason — recorded in the audit log."}},"type":"object","required":["reason"],"title":"RankingConfigOverrideRequest","description":"Admin override for one (tenant, capability) ranking config row.\n\nEither field may be set — ``status`` to force a specific lifecycle\nstate (e.g. roll a misbehaving tenant back to 'disabled'), or\n``clear_override=true`` to hand the row back to the evaluator\n(clears ``manually_overridden`` and resumes nightly governance)."},"RankingConfigRowResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"capability":{"type":"string","title":"Capability"},"status":{"type":"string","title":"Status"},"trigger_met_since":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Trigger Met Since"},"enabled_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Enabled At"},"manually_overridden":{"type":"boolean","title":"Manually Overridden"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["tenant_id","capability","status","manually_overridden","updated_at"],"title":"RankingConfigRowResponse","description":"One row from tenant_ranking_config."},"ReasonCountResponse":{"properties":{"code":{"type":"string","title":"Code"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["code","count"],"title":"ReasonCountResponse"},"RecentFeedbackResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/RecentFeedbackRowResponse"},"type":"array","title":"Rows"},"total_count":{"type":"integer","title":"Total Count"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["rows","total_count","limit","offset"],"title":"RecentFeedbackResponse"},"RecentFeedbackRowResponse":{"properties":{"feedback_id":{"type":"string","format":"uuid","title":"Feedback Id"},"query_id":{"type":"string","format":"uuid","title":"Query Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"rating":{"type":"string","title":"Rating"},"reason_codes":{"items":{"type":"string"},"type":"array","title":"Reason Codes"},"free_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Free Text"},"free_text_pii_scrubbed":{"type":"boolean","title":"Free Text Pii Scrubbed"},"missing_source_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Missing Source Hint"},"ui_bug_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ui Bug Category"},"synthesis_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Synthesis Model"},"reranker_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reranker Version"},"answer_text_hash":{"type":"string","title":"Answer Text Hash"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["feedback_id","query_id","tenant_id","user_id","rating","reason_codes","free_text","free_text_pii_scrubbed","missing_source_hint","ui_bug_category","synthesis_model","reranker_version","answer_text_hash","created_at"],"title":"RecentFeedbackRowResponse"},"RecentQueriesResponse":{"properties":{"window_hours":{"type":"integer","title":"Window Hours"},"since":{"type":"string","format":"date-time","title":"Since"},"count":{"type":"integer","title":"Count"},"items":{"items":{"$ref":"#/components/schemas/RecentQueryItem"},"type":"array","title":"Items"}},"type":"object","required":["window_hours","since","count","items"],"title":"RecentQueriesResponse"},"RecentQueryItem":{"properties":{"created_at":{"type":"string","format":"date-time","title":"Created At"},"api_key_id":{"type":"string","format":"uuid","title":"Api Key Id"},"auth_source":{"type":"string","title":"Auth Source"},"tenant_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"},"employee_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Email"},"query_hash_prefix":{"type":"string","title":"Query Hash Prefix"},"taxonomy_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Domain"},"result_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Result Count"},"status":{"type":"string","title":"Status"}},"type":"object","required":["created_at","api_key_id","auth_source","tenant_id","tenant_name","employee_email","query_hash_prefix","taxonomy_domain","result_count","status"],"title":"RecentQueryItem"},"RechunkByChunkerVersionRequest":{"properties":{"versions":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Versions","description":"List of chunker_version strings to re-enrich. Typical values: [\"two_tier_v1_token_boundary\", \"two_tier_v2_sentence_aware_no_overlap\"]. Pieces whose children carry ANY of these versions are queued."},"tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Id","description":"UUID string. When set, the action is scoped to one tenant. When None, it spans every tenant in the environment."},"dry_run":{"type":"boolean","title":"Dry Run","description":"When true (default) the action returns the count + sample of pieces that WOULD be re-queued without mutating anything. Flip to false to actually set enrichment_due_at.","default":true},"limit":{"type":"integer","maximum":10000.0,"minimum":1.0,"title":"Limit","description":"Cap on number of pieces touched in one call. Iterate by re-calling until matched_count ≤ limit.","default":1000}},"type":"object","required":["versions"],"title":"RechunkByChunkerVersionRequest","description":"Body for POST /admin/maintenance/rechunk-by-chunker-version."},"RechunkByChunkerVersionResponse":{"properties":{"matched_count":{"type":"integer","title":"Matched Count"},"queued_count":{"type":"integer","title":"Queued Count"},"sample_piece_ids":{"items":{"type":"string"},"type":"array","title":"Sample Piece Ids"},"dry_run":{"type":"boolean","title":"Dry Run"},"audit_log_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Audit Log Id"}},"type":"object","required":["matched_count","queued_count","sample_piece_ids","dry_run","audit_log_id"],"title":"RechunkByChunkerVersionResponse","description":"Response for POST /admin/maintenance/rechunk-by-chunker-version."},"RedeemCodeErrorResponse":{"properties":{"success":{"type":"boolean","const":false,"title":"Success"},"error_code":{"type":"string","enum":["CODE_NOT_FOUND","CODE_EXPIRED","ALREADY_REDEEMED","CANNOT_REDEEM_OWN_CODE","CAP_REACHED"],"title":"Error Code"},"message":{"type":"string","title":"Message"}},"type":"object","required":["success","error_code","message"],"title":"RedeemCodeErrorResponse"},"RedeemCodeRequest":{"properties":{"code":{"type":"string","maxLength":16,"minLength":1,"title":"Code"}},"type":"object","required":["code"],"title":"RedeemCodeRequest"},"RedeemCodeSuccessResponse":{"properties":{"success":{"type":"boolean","const":true,"title":"Success"},"gb_awarded":{"type":"integer","title":"Gb Awarded"},"referral_id":{"type":"string","format":"uuid","title":"Referral Id"}},"type":"object","required":["success","gb_awarded","referral_id"],"title":"RedeemCodeSuccessResponse"},"ReferralStats":{"properties":{"total_gb_earned_year":{"type":"integer","title":"Total Gb Earned Year"},"total_referrals_year":{"type":"integer","title":"Total Referrals Year"},"cap_remaining_year":{"type":"integer","title":"Cap Remaining Year"},"pending_count":{"type":"integer","title":"Pending Count"},"cap_resets_at":{"type":"string","format":"date-time","title":"Cap Resets At"}},"type":"object","required":["total_gb_earned_year","total_referrals_year","cap_remaining_year","pending_count","cap_resets_at"],"title":"ReferralStats"},"ReferralSummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"referee_email":{"type":"string","format":"email","title":"Referee Email"},"status":{"type":"string","enum":["sent","clicked","signed_up","converted","rewarded","expired","revoked"],"title":"Status"},"channel":{"type":"string","enum":["email","link"],"title":"Channel"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"rewarded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rewarded At"},"gb_awarded_referrer":{"type":"integer","title":"Gb Awarded Referrer"},"gb_awarded_referee":{"type":"integer","title":"Gb Awarded Referee"}},"type":"object","required":["id","referee_email","status","channel","created_at","expires_at","rewarded_at","gb_awarded_referrer","gb_awarded_referee"],"title":"ReferralSummary"},"RefundRequest":{"properties":{"reason":{"type":"string","maxLength":1000,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"RefundRequest","description":"Phase A spec §1951 — body is ``{reason: string}``."},"RefundResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"stripe_refund_id":{"type":"string","title":"Stripe Refund Id"},"stripe_charge_id":{"type":"string","title":"Stripe Charge Id"},"refunded_amount_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Refunded Amount Usd"},"credit_id":{"type":"string","title":"Credit Id"},"issued_at":{"type":"string","format":"date-time","title":"Issued At"},"idempotent_replay":{"type":"boolean","title":"Idempotent Replay"}},"type":"object","required":["tenant_id","stripe_refund_id","stripe_charge_id","refunded_amount_usd","credit_id","issued_at","idempotent_replay"],"title":"RefundResponse"},"RelationshipPieceSchema":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"title":{"type":"string","title":"Title"},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"},"author":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author"},"source_type":{"type":"string","title":"Source Type"},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At"},"taxonomy_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Domain"},"taxonomy_subdomain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Subdomain"},"authority_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Authority Score"},"chunk_id":{"type":"string","title":"Chunk Id"}},"type":"object","required":["content_piece_id","title","source_type","chunk_id"],"title":"RelationshipPieceSchema","description":"One side of a relationship edge — the content piece + its chunk.\n\nSurfaces everything the frontend needs to render the card without a\nfollow-up request: title + source link, attribution, taxonomy, and\nthe specific chunk id on this side of the edge so a downstream\nSource Detail call can hydrate the excerpt.\n\nFields that may be missing on the underlying piece (``author``,\n``published_at``, ``taxonomy_domain``, ``taxonomy_subdomain``,\n``authority_score``) are nullable; the field is set only when the\npiece carries a populated value. ``source_type`` falls back to\n``\"upload\"`` for direct-upload pieces (no connector)."},"RelationshipSchema":{"properties":{"edge_id":{"type":"string","title":"Edge Id"},"edge_type":{"type":"string","enum":["agreement","disagreement","complementary","superseding"],"title":"Edge Type"},"confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Confidence"},"edge_created_at":{"type":"string","format":"date-time","title":"Edge Created At"},"piece_a":{"$ref":"#/components/schemas/RelationshipPieceSchema"},"piece_b":{"$ref":"#/components/schemas/RelationshipPieceSchema"}},"type":"object","required":["edge_id","edge_type","edge_created_at","piece_a","piece_b"],"title":"RelationshipSchema","description":"A single cross-reference edge between two content pieces.\n\n``edge_type`` is one of the four canonical relationship types. The\nDB column is named ``relationship_type``; we expose it as\n``edge_type`` in the API to match the frontend vocabulary. The\n``confidence`` field is normalised from the DB column of the same\nname; ``None`` means the underlying edge predates confidence\ncapture (no row in production matches today, but kept nullable so\nthe contract is future-proof)."},"RelationshipsResponse":{"properties":{"total":{"type":"integer","title":"Total"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"},"relationships":{"items":{"$ref":"#/components/schemas/RelationshipSchema"},"type":"array","title":"Relationships"}},"type":"object","required":["total"],"title":"RelationshipsResponse","description":"GET /v1/enterprise/intelligence/relationships response.\n\n``total`` is the count of edges matching the filter set ignoring\npagination (powers the \"Showing N of M\" header). ``next_cursor`` is\nan opaque, base64-url string encoding ``(created_at_iso, edge_id)``;\nthe frontend passes it back verbatim on the next request and never\ndecodes it."},"ReportUsageResponse":{"properties":{"tenants_processed":{"type":"integer","title":"Tenants Processed"},"rows_processed":{"type":"integer","title":"Rows Processed"},"total_tokens_reported":{"type":"integer","title":"Total Tokens Reported"},"rows_skipped_no_customer":{"type":"integer","title":"Rows Skipped No Customer"},"rows_skipped_unknown_operation":{"type":"integer","title":"Rows Skipped Unknown Operation","default":0},"rows_failed":{"type":"integer","title":"Rows Failed"},"skipped_sandbox":{"type":"boolean","title":"Skipped Sandbox"}},"type":"object","required":["tenants_processed","rows_processed","total_tokens_reported","rows_skipped_no_customer","rows_failed","skipped_sandbox"],"title":"ReportUsageResponse","description":"POST /v1/enterprise/billing/report-usage response (admin-only)."},"RerankerBreakdownResponse":{"properties":{"reranker_version":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reranker Version"},"thumbs_up":{"type":"integer","title":"Thumbs Up"},"thumbs_down":{"type":"integer","title":"Thumbs Down"},"total":{"type":"integer","title":"Total"},"thumb_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Thumb Rate"}},"type":"object","required":["reranker_version","thumbs_up","thumbs_down","total","thumb_rate"],"title":"RerankerBreakdownResponse"},"ResetPiecePermissionsResponse":{"properties":{"piece_id":{"type":"string","title":"Piece Id","description":"Content piece UUID that was reset."},"tenant_id":{"type":"string","title":"Tenant Id","description":"Tenant UUID the piece belongs to."},"permission_emails":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails","description":"Post-reset ``permission_emails`` (the source-resolved ACL restored from the audit log). ``None`` for legacy NULL rows."},"permission_resolution_mode":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Permission Resolution Mode","description":"Post-reset resolution mode (typically the original connector resolver's stamp: ``per_file_acl``, ``workspace_inherited``, ``uploader_only``, etc.). ``None`` for legacy NULL rows."},"permission_resolved_at":{"type":"string","title":"Permission Resolved At","description":"ISO-8601 timestamp of the reset (``now()`` at request time)."},"permission_emails_before":{"items":{"type":"string"},"type":"array","title":"Permission Emails Before","description":"Pre-reset ``permission_emails`` snapshot (the manual_override list the operator had stamped)."},"resolution_mode_before":{"type":"string","title":"Resolution Mode Before","description":"Pre-reset resolution mode — always ``manual_override``."}},"type":"object","required":["piece_id","tenant_id","permission_emails","permission_resolution_mode","permission_resolved_at","permission_emails_before","resolution_mode_before"],"title":"ResetPiecePermissionsResponse","description":"``DELETE /v1/enterprise/library/{piece_id}/permissions`` response.\n\nReturned to the dashboard after an admin invokes \"Reset to source\"\non a piece currently in ``permission_resolution_mode='manual_override'``.\nThe piece's ``permission_emails`` are restored from the most recent\nforward audit row's ``permission_emails_before`` snapshot — same\ncontract the nightly ``revert_expired_manual_overrides`` cron uses\nat override expiry. Both ``permission_emails_after`` and\n``resolution_mode_after`` may legitimately be ``None`` when the\npiece was created pre-B+ (no resolution mode was ever stamped)."},"ResumeBuildingResponse":{"properties":{"user_memory_rows_resumed":{"type":"integer","title":"User Memory Rows Resumed"},"memory_building_stopped":{"type":"boolean","title":"Memory Building Stopped"}},"type":"object","required":["user_memory_rows_resumed","memory_building_stopped"],"title":"ResumeBuildingResponse","description":"Response from POST /me/memory/resume-building.\n\n``user_memory_rows_resumed`` is 0 when the caller has never had\nmemory built about them (no row exists) and 1 when an existing row\nwas flipped back to ``memory_building_stopped=False``. Both outcomes\nare 200 OK — neither is an error condition."},"RevokeApiKeyRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason","description":"Optional human-readable reason for the revocation, stored in the audit log metadata."}},"additionalProperties":false,"type":"object","title":"RevokeApiKeyRequest","description":"Body for ``POST /v1/enterprise/api-keys/{key_id}/revoke``."},"RevokeApiKeyResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"revoked_at":{"type":"string","format":"date-time","title":"Revoked At"}},"type":"object","required":["id","revoked_at"],"title":"RevokeApiKeyResponse"},"RevokeOthersResponse":{"properties":{"revoked_count":{"type":"integer","minimum":0.0,"title":"Revoked Count","description":"Number of sessions that were active and got revoked."}},"type":"object","required":["revoked_count"],"title":"RevokeOthersResponse"},"RevokeReferralRequest":{"properties":{"reason":{"anyOf":[{"type":"string","maxLength":40},{"type":"null"}],"title":"Reason"}},"type":"object","title":"RevokeReferralRequest","description":"Optional body for DELETE /v1/referrals/{id}.\n\nNot part of the public API contract — only consumed if FastAPI's\nbody parser sees JSON. Mostly here so the audit log records a\nreason when the dashboard surfaces one."},"RolloutMetricBlockResponse":{"properties":{"total_rows":{"type":"integer","title":"Total Rows"},"v2_rows":{"type":"integer","title":"V2 Rows"},"adoption_rate":{"type":"number","title":"Adoption Rate"},"grounding_ran_rate":{"type":"number","title":"Grounding Ran Rate"},"brain_memory_injected_rate":{"type":"number","title":"Brain Memory Injected Rate"},"supersession_firing_rate":{"type":"number","title":"Supersession Firing Rate"}},"type":"object","required":["total_rows","v2_rows","adoption_rate","grounding_ran_rate","brain_memory_injected_rate","supersession_firing_rate"],"title":"RolloutMetricBlockResponse","description":"Counts + 0..1 rates for one scope (overall or a single tenant)."},"RolloutMetricsResponse":{"properties":{"from":{"type":"string","title":"From"},"to":{"type":"string","title":"To"},"overall":{"$ref":"#/components/schemas/RolloutMetricBlockResponse"},"per_tenant":{"items":{"$ref":"#/components/schemas/RolloutTenantMetricResponse"},"type":"array","title":"Per Tenant"}},"type":"object","required":["from","to","overall","per_tenant"],"title":"RolloutMetricsResponse","description":"Synthesis v2 Rollout Status metrics over a window.\n\n``adoption_rate`` is over all rows; the three signal rates are over v2\nrows only (a legacy row cannot fire a v2 signal). Rates are 0..1 floats."},"RolloutTenantMetricResponse":{"properties":{"total_rows":{"type":"integer","title":"Total Rows"},"v2_rows":{"type":"integer","title":"V2 Rows"},"adoption_rate":{"type":"number","title":"Adoption Rate"},"grounding_ran_rate":{"type":"number","title":"Grounding Ran Rate"},"brain_memory_injected_rate":{"type":"number","title":"Brain Memory Injected Rate"},"supersession_firing_rate":{"type":"number","title":"Supersession Firing Rate"},"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"}},"type":"object","required":["total_rows","v2_rows","adoption_rate","grounding_ran_rate","brain_memory_injected_rate","supersession_firing_rate","tenant_id"],"title":"RolloutTenantMetricResponse","description":"Per-tenant metric block plus its tenant identity."},"S3ConnectRequest":{"properties":{"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","default":"S3 Bucket"},"mode":{"type":"string","const":"cross_account","title":"Mode","description":"Always 'cross_account'. The 'managed' mode was removed in W15.","default":"cross_account"},"bucket_name":{"type":"string","maxLength":63,"minLength":1,"title":"Bucket Name","description":"Customer's S3 bucket name. Must follow AWS bucket naming rules."},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix","description":"Only index files under this S3 key prefix."},"role_arn":{"type":"string","maxLength":2048,"minLength":1,"title":"Role Arn","description":"IAM role ARN for cross-account access. Quelvio assumes this role to read the bucket."},"file_extensions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"File Extensions","description":"File extensions to include. Defaults to ['.pdf','.docx','.pptx','.xlsx','.csv','.tsv','.txt','.md']."},"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails","description":"Who can retrieve chunks ingested from this S3 connector. Omit (or null) to default to the operator setting up the connector. Pass the literal '__public_tenant__' for tenant-public access. Pass a list of tenant-member emails, '@domain' entries, or SSO group addresses for explicit ACL."}},"type":"object","required":["bucket_name","role_arn"],"title":"S3ConnectRequest","description":"POST /v1/enterprise/connectors/s3/connect request body.\n\nCross-account mode only. The legacy ``\"managed\"`` mode (Quelvio\ncreates a per-tenant bucket) was removed in v0.5.15 W15 CRIT #6 —\npilots without an existing S3 bucket use the Upload connector\ninstead. The ``mode`` field is retained as a Literal for forward\ncompatibility but only ``\"cross_account\"`` is accepted."},"S3ConnectResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"bucket_name":{"type":"string","title":"Bucket Name"},"status":{"type":"string","title":"Status"}},"type":"object","required":["connector_id","bucket_name","status"],"title":"S3ConnectResponse","description":"POST /v1/enterprise/connectors/s3/connect response."},"S3DetectRegionRequest":{"properties":{"bucket_name":{"type":"string","maxLength":63,"minLength":3,"title":"Bucket Name"}},"type":"object","required":["bucket_name"],"title":"S3DetectRegionRequest","description":"POST /v1/enterprise/connectors/s3/detect-region request body.\n\nBest-effort auto-detection of the bucket's region by HEAD-ing the\npublic S3 endpoint and reading ``x-amz-bucket-region``. Used by\nStep 1 of the wizard to pre-fill the region dropdown."},"S3DetectRegionResponse":{"properties":{"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region","description":"Region name (e.g. 'eu-west-1') or null if undetectable."},"reason":{"anyOf":[{"type":"string","enum":["bucket_private","bucket_not_found","network_error","ok"]},{"type":"null"}],"title":"Reason","description":"Discriminator on the failure mode when region is null. 'ok' on success."}},"type":"object","title":"S3DetectRegionResponse","description":"POST /v1/enterprise/connectors/s3/detect-region response.\n\nAlways 200 — failure modes are encoded as ``region: null`` with a\n``reason`` discriminator so the frontend can fall back to manual\nregion selection silently. The wizard does NOT show error UI for\nthis endpoint; the customer wouldn't know what to do with\n\"bucket_private\" anyway."},"S3ObjectItem":{"properties":{"key":{"type":"string","title":"Key"},"size_bytes":{"type":"integer","minimum":0.0,"title":"Size Bytes"},"last_modified":{"type":"string","format":"date-time","title":"Last Modified"}},"type":"object","required":["key","size_bytes","last_modified"],"title":"S3ObjectItem","description":"A single object returned by the picker preview."},"S3ObjectsRequest":{"properties":{"connector_id":{"type":"string","title":"Connector Id","description":"Connector_id whose bucket to list. Must belong to the caller's tenant."},"prefix":{"type":"string","maxLength":1024,"title":"Prefix","description":"S3 key prefix to filter by. Empty string lists from the bucket root. Trailing slash is conventional for folder-like browsing.","default":""}},"type":"object","required":["connector_id"],"title":"S3ObjectsRequest","description":"POST /v1/enterprise/connectors/s3/objects — picker preview (Day 4e).\n\nStep 4 of the wizard lists the customer's bucket so they can pick\nfolders/prefixes to sync. Read-only; no IAM mutation."},"S3ObjectsResponse":{"properties":{"objects":{"items":{"$ref":"#/components/schemas/S3ObjectItem"},"type":"array","title":"Objects","description":"Up to 100 objects matching the prefix. Folders are not synthesised — keys are listed verbatim from S3."},"truncated":{"type":"boolean","title":"Truncated","description":"True if more than 100 objects matched. Use ``next_continuation_token`` to fetch the next page.","default":false},"next_continuation_token":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Continuation Token","description":"Opaque AWS token. Pass to a future call to resume listing (NOT WIRED at v0.6 — Step 4 caps at 100; v0.7 may add pagination)."},"failure":{"anyOf":[{"$ref":"#/components/schemas/S3VerifyFailureSchema"},{"type":"null"}],"description":"Populated when assume_role fails. The wizard surfaces failure.customer_action verbatim, same as verify."}},"type":"object","title":"S3ObjectsResponse","description":"POST /v1/enterprise/connectors/s3/objects response.\n\nOn assume_role failure surfaces a ``failure`` field with one of the\neight ``S3VerifyFailureSchema`` codes — same shape as the verify\npolling response so the wizard's Step 4 error UX can re-use the\nsame component."},"S3RotateExternalIdResponse":{"properties":{"external_id":{"type":"string","title":"External Id","description":"Freshly-rotated per-tenant ExternalId for the customer's IAM trust policy. Shown ONCE — copy now, you cannot retrieve it later. The previous ExternalId is invalidated immediately."},"quelvio_principal_arn":{"type":"string","title":"Quelvio Principal Arn","description":"ARN the customer adds as the trusted principal — unchanged across rotations (same as setup-init)."},"trust_policy_template":{"type":"string","title":"Trust Policy Template","description":"Pre-rendered trust policy JSON with the NEW ExternalId. The customer pastes this into their IAM role's trust relationships, replacing the prior policy."},"permissions_policy_template":{"type":"string","title":"Permissions Policy Template","description":"Pre-rendered permissions policy JSON — unchanged across rotations. Surfaced for completeness so the wizard can render the same Step 2 layout in setup vs reconfigure mode."}},"type":"object","required":["external_id","quelvio_principal_arn","trust_policy_template","permissions_policy_template"],"title":"S3RotateExternalIdResponse","description":"POST /v1/enterprise/connectors/{id}/s3/rotate-external-id — response.\n\nReturned to the wizard's reconfigure-mode Step 2. ExternalId appears\nin this response ONLY — there is no GET endpoint that surfaces it\nagain. If the customer loses the value before pasting it into their\nAWS trust policy, they call rotate-external-id again (each call\ninvalidates the previous ExternalId immediately, no grace window)."},"S3SetupInitRequest":{"properties":{"bucket_name":{"type":"string","maxLength":63,"minLength":1,"title":"Bucket Name","description":"Customer's S3 bucket name. AWS bucket naming rules apply."},"region":{"anyOf":[{"type":"string","maxLength":32},{"type":"null"}],"title":"Region","description":"Bucket region. Optional — Quelvio auto-detects via GetBucketLocation if omitted (Step 4)."},"kms_key_arn":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Kms Key Arn","description":"KMS key ARN if the bucket uses SSE-KMS encryption. Triggers the kms:Decrypt section in the permissions policy template."},"name":{"type":"string","maxLength":255,"minLength":1,"title":"Name","description":"Human-readable label shown in Sources.","default":"S3 Bucket"},"permission_emails":{"anyOf":[{"type":"string","const":"__public_tenant__"},{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Permission Emails","description":"Who can retrieve chunks ingested from this S3 connector. Omit (or null) to default to the operator setting up the connector. Pass the literal '__public_tenant__' for tenant-public access. Pass a list of tenant-member emails, '@domain' entries, or SSO group addresses for explicit ACL."}},"type":"object","required":["bucket_name"],"title":"S3SetupInitRequest","description":"POST /v1/enterprise/connectors/s3/setup-init — Step 1 of the v0.6\nwizard. Persists the connector row in pending_role_setup state and\nreturns the per-tenant ExternalId + the trust policy JSON template\nthe customer pastes into their AWS role.\n\nThe ExternalId is shown to the customer ONCE in the response.\nSubsequent reads of the connector return external_id_set: true only."},"S3SetupInitResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"external_id":{"type":"string","title":"External Id","description":"Per-tenant ExternalId for the customer's IAM trust policy. Shown ONCE — copy now, you cannot retrieve it later."},"quelvio_principal_arn":{"type":"string","title":"Quelvio Principal Arn","description":"ARN the customer adds as the trusted principal in their role's trust policy. Same value across all customers in the same env."},"trust_policy_template":{"type":"string","title":"Trust Policy Template","description":"Pre-rendered trust policy JSON, ExternalId + principal substituted. The customer pastes this verbatim into their IAM role's trust relationships."},"permissions_policy_template":{"type":"string","title":"Permissions Policy Template","description":"Pre-rendered permissions policy JSON. Includes s3:GetObject + s3:ListBucket on the bucket; kms:Decrypt on the KMS key if kms_key_arn was provided."}},"type":"object","required":["connector_id","external_id","quelvio_principal_arn","trust_policy_template","permissions_policy_template"],"title":"S3SetupInitResponse","description":"POST /v1/enterprise/connectors/s3/setup-init — response.\n\nThe ``external_id`` field appears in this response ONLY. No GET\nendpoint surfaces it again. If the customer loses the value before\ncompleting setup, they delete the connector and re-init."},"S3TestRequest":{"properties":{"mode":{"type":"string","const":"cross_account","title":"Mode","description":"Always 'cross_account' — managed mode was removed in W15.","default":"cross_account"},"bucket_name":{"type":"string","maxLength":63,"minLength":1,"title":"Bucket Name"},"prefix":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Prefix"},"role_arn":{"type":"string","maxLength":2048,"minLength":1,"title":"Role Arn"}},"type":"object","required":["bucket_name","role_arn"],"title":"S3TestRequest","description":"POST /v1/enterprise/connectors/s3/test request body — v0.5.15 W15 HIGH #10.\n\nSame shape as :class:`S3ConnectRequest` minus ``name`` and\n``file_extensions`` (those are display / filtering metadata that\ndon't affect whether the connection works). The endpoint validates\nthe role + bucket WITHOUT creating an EnterpriseConnector row, so\nthe operator can iterate on the IAM setup until the test passes,\nTHEN click Connect to commit the working config."},"S3TestResponse":{"properties":{"success":{"type":"boolean","title":"Success"},"region":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Region","description":"Bucket's AWS region (auto-discovered). Null on failure."},"object_count_sample":{"type":"integer","title":"Object Count Sample","description":"Number of objects matched in the preview sample (≤ 5).","default":0},"preview":{"items":{"type":"string"},"type":"array","title":"Preview","description":"Up to 5 example object keys matching the prefix. Empty on failure."},"message":{"type":"string","title":"Message","description":"Friendly message — 'Connected successfully', or actionable error text."},"error_kind":{"anyOf":[{"type":"string","enum":["role_assume_failed","access_denied","no_such_bucket","wrong_region","unknown"]},{"type":"null"}],"title":"Error Kind","description":"Categorised error kind for frontend mapping. Null on success."}},"type":"object","required":["success","message"],"title":"S3TestResponse","description":"POST /v1/enterprise/connectors/s3/test response — v0.5.15 W15 HIGH #10.\n\nOn success: returns the bucket's region (auto-discovered),\na small sample of object keys under the configured prefix,\nand the count of the sample. ``error_kind`` is null on success.\n\nOn failure: ``success=False`` with a human-readable ``message`` and\na structured ``error_kind`` for frontend mapping (e.g. show a\n\"Trust policy\" hint for ``role_assume_failed``)."},"S3VerifyFailureSchema":{"properties":{"code":{"type":"string","title":"Code","description":"Stable failure code — one of the eight from s3_preflight.ALL_FAILURE_CODES. Frontends pin these."},"customer_action":{"type":"string","title":"Customer Action","description":"Customer-action text to render verbatim in the wizard. Never include AWS internals or stack traces here."}},"type":"object","required":["code","customer_action"],"title":"S3VerifyFailureSchema","description":"Serialised PreflightFailure for the polling response."},"S3VerifyJobAcceptedResponse":{"properties":{"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","const":"pending","title":"Status","default":"pending"}},"type":"object","required":["job_id"],"title":"S3VerifyJobAcceptedResponse","description":"POST /v1/enterprise/connectors/s3/verify — initial response.\n\nReturns immediately after enqueuing the background preflight task.\nThe wizard polls GET /verify/{job_id} until terminal."},"S3VerifyRequest":{"properties":{"connector_id":{"type":"string","title":"Connector Id","description":"Connector_id returned from /setup-init."},"customer_role_arn":{"type":"string","maxLength":2048,"minLength":1,"title":"Customer Role Arn","description":"ARN of the IAM role the customer created. Must be in the form arn:aws:iam::123456789012:role/<role-name>."}},"type":"object","required":["connector_id","customer_role_arn"],"title":"S3VerifyRequest","description":"POST /v1/enterprise/connectors/s3/verify — Step 3 of the wizard.\n\nAdds the customer's role ARN to the allowlist and starts an async\npreflight job. Returns a job_id immediately. The wizard polls\nGET /verify/{job_id} every 2s until status is 'success' or 'failed'."},"S3VerifyStatusResponse":{"properties":{"job_id":{"type":"string","title":"Job Id"},"status":{"type":"string","enum":["pending","success","failed"],"title":"Status"},"elapsed_ms":{"type":"integer","title":"Elapsed Ms","description":"Wall time since the verify job started."},"current_state_message":{"type":"string","title":"Current State Message","description":"Progressive UX string mapped from elapsed_ms. The wizard renders this verbatim while status is pending. On terminal states this carries either a success confirmation or the failure.customer_action text."},"failure":{"anyOf":[{"$ref":"#/components/schemas/S3VerifyFailureSchema"},{"type":"null"}],"description":"Populated when status == 'failed'."}},"type":"object","required":["job_id","status","elapsed_ms","current_state_message"],"title":"S3VerifyStatusResponse","description":"GET /v1/enterprise/connectors/s3/verify/{job_id} — polling response."},"SPOFListResponse":{"properties":{"risks":{"items":{"$ref":"#/components/schemas/SPOFRiskSchema"},"type":"array","title":"Risks"},"total":{"type":"integer","title":"Total"}},"type":"object","required":["risks","total"],"title":"SPOFListResponse","description":"GET /v1/enterprise/intelligence/spof-risks response."},"SPOFRiskSchema":{"properties":{"domain":{"type":"string","title":"Domain"},"sole_expert_email":{"type":"string","title":"Sole Expert Email"},"sole_expert_name":{"type":"string","title":"Sole Expert Name"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"},"document_count":{"type":"integer","title":"Document Count"},"domain_total_docs":{"type":"integer","title":"Domain Total Docs"},"concentration":{"type":"number","title":"Concentration"},"closest_alternate_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closest Alternate Email"},"closest_alternate_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closest Alternate Name"},"taxonomy_overlap_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Taxonomy Overlap Pct"}},"type":"object","required":["domain","sole_expert_email","sole_expert_name","document_count","domain_total_docs","concentration"],"title":"SPOFRiskSchema","description":"Single-point-of-failure knowledge risk."},"SamlConfigResponse":{"properties":{"saml_enabled":{"type":"boolean","title":"Saml Enabled"},"dns_verified":{"type":"boolean","title":"Dns Verified"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"idp_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Type"},"idp_entity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Entity Id"},"idp_acs_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Acs Url"},"idp_signin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Signin Url"},"idp_certificate_fingerprint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Certificate Fingerprint"},"saml_attribute_mapping":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Saml Attribute Mapping"}},"type":"object","required":["saml_enabled","dns_verified","domain","idp_type","idp_entity_id","idp_acs_url","idp_signin_url","idp_certificate_fingerprint","saml_attribute_mapping"],"title":"SamlConfigResponse"},"SamlConfigUploadRequest":{"properties":{"idp_type":{"type":"string","title":"Idp Type","description":"okta|azure_ad|google_workspace|custom"},"idp_metadata_xml":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Metadata Xml"},"idp_entity_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Entity Id"},"idp_acs_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Acs Url"},"idp_signin_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Signin Url"},"idp_certificate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Idp Certificate"},"saml_attribute_mapping":{"anyOf":[{"additionalProperties":{"type":"string"},"type":"object"},{"type":"null"}],"title":"Saml Attribute Mapping"}},"type":"object","required":["idp_type"],"title":"SamlConfigUploadRequest","description":"Customer admin uploads IdP details, either via parsed metadata XML\nor as raw fields. saml_enabled stays false until /enable is called."},"SamlEnableRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"SamlEnableRequest"},"ScopeChangeRequest":{"properties":{"scope_keys":{"items":{"type":"string"},"type":"array","title":"Scope Keys"}},"type":"object","required":["scope_keys"],"title":"ScopeChangeRequest","description":"PATCH /v1/enterprise/sources/{id}/scope request body.\n\n``scope_keys`` is the flat list of canonical ScopePickerNode keys\n(whatever the adapter emits — e.g. Slack channel IDs, Teams\n``team_id:channel_id`` composites, Dropbox path_lower strings).\nTrack-I frontend is the source of truth for the list shape; the\nserver translates it into the connector-specific config shape via\n``apply_scope_keys_to_config``."},"ScopeChangeResponse":{"properties":{"removed_pieces_count":{"type":"integer","title":"Removed Pieces Count"},"added_scope_count":{"type":"integer","title":"Added Scope Count"},"updated_scope_count":{"type":"integer","title":"Updated Scope Count"},"removed_scope_count":{"type":"integer","title":"Removed Scope Count"},"cleanup_deferred":{"type":"boolean","title":"Cleanup Deferred"}},"type":"object","required":["removed_pieces_count","added_scope_count","updated_scope_count","removed_scope_count","cleanup_deferred"],"title":"ScopeChangeResponse","description":"PATCH /v1/enterprise/sources/{id}/scope response."},"ScopeEntry":{"properties":{"type":{"type":"string","enum":["domain","connector","path_prefix"],"title":"Type"},"value":{"type":"string","maxLength":1024,"minLength":1,"title":"Value"}},"additionalProperties":false,"type":"object","required":["type","value"],"title":"ScopeEntry","description":"One entry in a service account's corpus scope.\n\nSee ``application/service_accounts/scope.py`` for the supported\ntypes + validation rules."},"ScopePreviewResponse":{"properties":{"pieces_to_delete_count":{"type":"integer","title":"Pieces To Delete Count"},"cleanup_deferred":{"type":"boolean","title":"Cleanup Deferred"}},"type":"object","required":["pieces_to_delete_count","cleanup_deferred"],"title":"ScopePreviewResponse","description":"GET /v1/enterprise/sources/{id}/scope-preview response."},"ScopedOverrideResponse":{"properties":{"id":{"type":"string","title":"Id"},"flag_key":{"type":"string","title":"Flag Key"},"tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Id"},"user_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"User Id"},"value":{"type":"string","title":"Value"},"scope":{"type":"string","title":"Scope"},"set_by_email":{"type":"string","title":"Set By Email"},"set_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Set At"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["id","flag_key","value","scope","set_by_email"],"title":"ScopedOverrideResponse","description":"One scoped override row."},"ScopedOverridesListResponse":{"properties":{"flag_key":{"type":"string","title":"Flag Key"},"overrides":{"items":{"$ref":"#/components/schemas/ScopedOverrideResponse"},"type":"array","title":"Overrides"}},"type":"object","required":["flag_key","overrides"],"title":"ScopedOverridesListResponse"},"SeatChangeRequest":{"properties":{"target_seat_count":{"type":"integer","minimum":0.0,"title":"Target Seat Count"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"}},"type":"object","required":["target_seat_count"],"title":"SeatChangeRequest","description":"Request body for POST /api/billing/seats/change."},"SeatStatusResponse":{"properties":{"plan_tier":{"type":"string","title":"Plan Tier"},"billing_period":{"type":"string","enum":["monthly","annual"],"title":"Billing Period"},"is_per_seat_billing":{"type":"boolean","title":"Is Per Seat Billing"},"active_members":{"type":"integer","title":"Active Members"},"pending_invites":{"type":"integer","title":"Pending Invites"},"reserved_member_slots":{"type":"integer","title":"Reserved Member Slots"},"paid_seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Paid Seats"},"effective_paid_seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Effective Paid Seats"},"available_seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Available Seats"},"scheduled_target_seats":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Scheduled Target Seats"}},"type":"object","required":["plan_tier","billing_period","is_per_seat_billing","active_members","pending_invites","reserved_member_slots"],"title":"SeatStatusResponse","description":"Response body for GET /api/billing/seats/status."},"SecurityPolicyResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"max_oauth_session_age_hours":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Oauth Session Age Hours"},"ip_allowlist_cidrs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Ip Allowlist Cidrs"},"max_pats_per_user":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Pats Per User"},"max_service_accounts_per_tenant":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Max Service Accounts Per Tenant"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["tenant_id"],"title":"SecurityPolicyResponse","description":"Current tenant security policy snapshot.\n\nEvery field is ``Optional`` — NULL renders to ``null`` in JSON and\nmeans \"no enforcement\". The UI translates NULL to a \"No limit\"\ntoggle."},"SendReferralRequest":{"properties":{"referee_email":{"type":"string","format":"email","title":"Referee Email"}},"type":"object","required":["referee_email"],"title":"SendReferralRequest"},"ServiceAccountResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"scopes":{"items":{"$ref":"#/components/schemas/ScopeEntry"},"type":"array","title":"Scopes"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"type":"string","title":"Created By"},"revoked_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Revoked At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"}},"additionalProperties":false,"type":"object","required":["id","name","description","scopes","created_at","created_by","revoked_at","last_used_at"],"title":"ServiceAccountResponse"},"SessionRow":{"properties":{"id":{"type":"string","title":"Id"},"client_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Client Label","description":"Human-readable label parsed from the User-Agent at issuance (e.g. 'Chrome 130 on macOS', 'Quelvio CLI v0.2.0'). May be null for sessions issued before cli-p7."},"client_kind":{"type":"string","title":"Client Kind","description":"One of 'browser' | 'cli' | 'unknown'. Drives the row icon in the dashboard. Stored loosely (TEXT) so a future kind does not require a destructive migration."},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At"},"is_current":{"type":"boolean","title":"Is Current","description":"True when this session matches the OAuth token used to make the request. The UI greys out the Revoke button on the current row to prevent self-lockout via this surface.","default":false}},"type":"object","required":["id","client_kind","created_at"],"title":"SessionRow","description":"One row in ``GET /me/sessions`` — safe to serialise over HTTP."},"SetInstrumentEnabledRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"SetInstrumentEnabledRequest","description":"Body for the PATCH that flips the kill switch."},"SetInstrumentEnabledResponseSchema":{"properties":{"instrument":{"type":"string","title":"Instrument"},"enabled":{"type":"boolean","title":"Enabled"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["instrument","enabled","updated_at"],"title":"SetInstrumentEnabledResponseSchema","description":"Response from the PATCH — echoes the new state + the timestamp."},"SetOverrideRequest":{"properties":{"value":{"type":"string","title":"Value","description":"New override value as a string."},"confirmation_phrase":{"type":"string","title":"Confirmation Phrase","description":"Must equal `confirm_phrase_to_override` from the GET listing. Guard against accidental clicks."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Optional free-text justification logged on the row."}},"type":"object","required":["value","confirmation_phrase"],"title":"SetOverrideRequest","description":"POST /v1/admin/feature-flags/{key} body."},"SetScopedOverrideRequest":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id","description":"Tenant the override applies to (required — every scoped override is at least tenant-scoped)."},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id","description":"If set, the override is user-tier (within the tenant); if omitted, it is tenant-tier."},"value":{"type":"string","title":"Value","description":"Override value as a string."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Optional free-text justification, logged on the row."},"confirmation_phrase":{"type":"string","title":"Confirmation Phrase","description":"Must equal `scope {key}` — the type-to-confirm guard."}},"type":"object","required":["tenant_id","value","confirmation_phrase"],"title":"SetScopedOverrideRequest","description":"POST /v1/admin/feature-flags/{key}/scoped body."},"SetupModeRequest":{"properties":{"mode":{"type":"string","enum":["everything","selective"],"title":"Mode","description":"'everything' skips the picker and ingests all reachable content. 'selective' shows the folder picker."}},"additionalProperties":false,"type":"object","required":["mode"],"title":"SetupModeRequest","description":"POST /v1/enterprise/connectors/{id}/setup-mode request body."},"SetupModeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"mode":{"type":"string","enum":["everything","selective"],"title":"Mode"},"has_root_files":{"type":"boolean","title":"Has Root Files"}},"type":"object","required":["connector_id","mode","has_root_files"],"title":"SetupModeResponse","description":"POST /v1/enterprise/connectors/{id}/setup-mode response."},"SetupPaymentRequest":{"properties":{"return_url":{"anyOf":[{"type":"string","maxLength":2048},{"type":"null"}],"title":"Return Url","description":"Optional Stripe Customer Portal return URL."}},"type":"object","title":"SetupPaymentRequest","description":"POST /v1/enterprise/billing/setup-payment request."},"SetupPaymentResponse":{"properties":{"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"SetupPaymentResponse","description":"POST /v1/enterprise/billing/setup-payment response.\n\nThe frontend redirects ``window.location.href = url`` to send the\ncustomer to the Stripe Customer Portal where they attach a card."},"SharepointAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"SharepointAuthUrlResponse","description":"GET /v1/enterprise/connect/sharepoint response."},"SharepointDriveItem":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"drive_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Drive Type"}},"type":"object","required":["id","name"],"title":"SharepointDriveItem","description":"A single document library (drive) inside a SharePoint site."},"SharepointScopeRequest":{"properties":{"sites":{"items":{"$ref":"#/components/schemas/SharepointScopeSiteItem"},"type":"array","title":"Sites"}},"type":"object","title":"SharepointScopeRequest","description":"POST /v1/enterprise/connectors/{id}/sharepoint/scope body.\n\nAdmin picks which sites and drives to sync in the site picker modal.\nAn empty ``drive_ids`` list on a site entry means \"all drives in this\nsite\" and is expanded at sync time."},"SharepointScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"scope":{"items":{"$ref":"#/components/schemas/SharepointScopeSiteItem"},"type":"array","title":"Scope"},"site_count":{"type":"integer","title":"Site Count"},"drive_count":{"type":"integer","title":"Drive Count"}},"type":"object","required":["connector_id","scope","site_count","drive_count"],"title":"SharepointScopeResponse","description":"POST /v1/enterprise/connectors/{id}/sharepoint/scope response."},"SharepointScopeSiteItem":{"properties":{"site_id":{"type":"string","maxLength":500,"minLength":1,"title":"Site Id"},"drive_ids":{"items":{"type":"string"},"type":"array","title":"Drive Ids"}},"type":"object","required":["site_id"],"title":"SharepointScopeSiteItem","description":"One entry in the connector's multi-site sync scope.\n\nAn empty ``drive_ids`` list means \"every drive in this site\" and is\nresolved to the concrete drive list at sync time."},"SharepointSiteItem":{"properties":{"id":{"type":"string","title":"Id"},"display_name":{"type":"string","title":"Display Name"},"web_url":{"type":"string","title":"Web Url"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"}},"type":"object","required":["id","display_name","web_url"],"title":"SharepointSiteItem","description":"A single SharePoint site returned by the picker."},"SharepointSitesResponse":{"properties":{"sites":{"items":{"$ref":"#/components/schemas/SharepointSiteItem"},"type":"array","title":"Sites"},"drives":{"items":{"$ref":"#/components/schemas/SharepointDriveItem"},"type":"array","title":"Drives"},"selected_site_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selected Site Id"},"selected_drive_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Selected Drive Id"},"selected_scope":{"items":{"$ref":"#/components/schemas/SharepointScopeSiteItem"},"type":"array","title":"Selected Scope"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["sites"],"title":"SharepointSitesResponse","description":"GET /v1/enterprise/connectors/{id}/sharepoint/sites response.\n\nReturns the list of sites the admin can pick from. If ``site_id`` is\nsupplied as a query param, also returns the drives within that site.\n``selected_scope`` reflects the multi-site selection persisted via\nthe /scope endpoint; the legacy ``selected_site_id`` /\n``selected_drive_id`` fields continue to echo the v0.4.2 single-drive\npick stored via /start-sync.\n\n``next_cursor`` carries the Graph ``@odata.nextLink`` URL when the\npage-bounded picker call (v0.5.22 Phase 2 — CC-9) hasn't exhausted\nthe sites collection. ``None`` when exhausted. The FE adapter\nre-fetches with ``?after=<next_cursor>`` until ``None``. Tenants\nwith <=1000 sites get the full list in the first response."},"SharepointStartSyncRequest":{"properties":{"site_id":{"type":"string","maxLength":500,"minLength":1,"title":"Site Id"},"drive_id":{"type":"string","maxLength":500,"minLength":1,"title":"Drive Id"}},"type":"object","required":["site_id","drive_id"],"title":"SharepointStartSyncRequest","description":"POST /v1/enterprise/connectors/{id}/sharepoint/start-sync body.\n\nAdmin has picked a site + drive in the UI; this endpoint persists the\nselection on the connector row, creates the Graph webhook subscription,\nand kicks off the first sync in the background."},"SharepointStartSyncResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"site_id":{"type":"string","title":"Site Id"},"drive_id":{"type":"string","title":"Drive Id"},"status":{"type":"string","title":"Status"},"webhook_subscription_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Webhook Subscription Id"},"webhook_expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Webhook Expires At"}},"type":"object","required":["connector_id","site_id","drive_id","status"],"title":"SharepointStartSyncResponse","description":"POST /v1/enterprise/connectors/{id}/sharepoint/start-sync response."},"SignalListResponse":{"properties":{"signals":{"items":{"$ref":"#/components/schemas/SignalSchema"},"type":"array","title":"Signals"},"total_unread":{"type":"integer","title":"Total Unread"}},"type":"object","required":["signals","total_unread"],"title":"SignalListResponse","description":"GET /v1/enterprise/signals response."},"SignalSchema":{"properties":{"id":{"type":"string","title":"Id"},"signal_type":{"type":"string","title":"Signal Type"},"severity":{"type":"string","title":"Severity"},"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"action_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Action Url"},"domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Domain"},"person_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Person Email"},"is_read":{"type":"boolean","title":"Is Read"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"expires_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Expires At"}},"type":"object","required":["id","signal_type","severity","title","description","is_read","created_at"],"title":"SignalSchema","description":"Single intelligence signal."},"SignalsIntelligenceResponse":{"properties":{"documents":{"type":"integer","title":"Documents"},"indexed_pct":{"type":"number","title":"Indexed Pct"},"experts":{"type":"integer","title":"Experts"},"gaps":{"type":"integer","title":"Gaps"},"intelligence_feed":{"items":{"$ref":"#/components/schemas/IntelligenceFeedItemSchema"},"type":"array","title":"Intelligence Feed"}},"type":"object","required":["documents","indexed_pct","experts","gaps"],"title":"SignalsIntelligenceResponse","description":"GET /v1/enterprise/intelligence/signals response.\n\nReturns dashboard stats AND an auto-generated intelligence feed so the\npage has content immediately, not just after the nightly cron run."},"SingleUploadResponse":{"properties":{"content_piece_id":{"type":"string","title":"Content Piece Id"},"status":{"type":"string","title":"Status"}},"type":"object","required":["content_piece_id","status"],"title":"SingleUploadResponse","description":"POST /v1/enterprise/content response."},"SlackAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"SlackAuthUrlResponse","description":"GET /v1/enterprise/connect/slack response."},"SlackChannelItem":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"is_private":{"type":"boolean","title":"Is Private"},"num_members":{"type":"integer","title":"Num Members"},"topic":{"type":"string","title":"Topic","default":""},"purpose":{"type":"string","title":"Purpose","default":""}},"type":"object","required":["id","name","is_private","num_members"],"title":"SlackChannelItem","description":"One channel the bot has access to (public or private).\n\n``num_members`` matters because the connector skips channels\nbelow the ``MIN_CHANNEL_MEMBERS`` threshold during\nhigh-signal-message scoring. Surfaced in the picker so admins\ncan see why a tiny channel is effectively read-only."},"SlackChannelsResponse":{"properties":{"channels":{"items":{"$ref":"#/components/schemas/SlackChannelItem"},"type":"array","title":"Channels"},"selected_channel_ids":{"items":{"type":"string"},"type":"array","title":"Selected Channel Ids"},"message_score_threshold":{"type":"integer","title":"Message Score Threshold"}},"type":"object","required":["channels","message_score_threshold"],"title":"SlackChannelsResponse","description":"GET /v1/enterprise/connectors/{id}/slack/channels response."},"SlackScopeRequest":{"properties":{"channel_ids":{"items":{"type":"string"},"type":"array","maxItems":500,"title":"Channel Ids"},"message_score_threshold":{"anyOf":[{"type":"integer","maximum":30.0,"minimum":1.0},{"type":"null"}],"title":"Message Score Threshold","description":"Override the score threshold for high-signal message indexing (default 5). Higher = less noise, lower = more coverage."}},"type":"object","title":"SlackScopeRequest","description":"POST /v1/enterprise/connectors/{id}/slack/scope body.\n\nAdmin picks the set of channels to index. Also optionally\noverrides the per-connector ``message_score_threshold`` —\ndefaults to 5 (see ``DEFAULT_MESSAGE_SCORE_THRESHOLD``) so\nteams can tune sensitivity without a code change."},"SlackScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"channel_ids":{"items":{"type":"string"},"type":"array","title":"Channel Ids"},"channel_count":{"type":"integer","title":"Channel Count"},"message_score_threshold":{"type":"integer","title":"Message Score Threshold"}},"type":"object","required":["connector_id","channel_ids","channel_count","message_score_threshold"],"title":"SlackScopeResponse","description":"POST /v1/enterprise/connectors/{id}/slack/scope response.\n\nNo subscription counters — Events API subscriptions are\nmanaged at the Slack App Console, not per-connector."},"SourceChunkSchema":{"properties":{"chunk_id":{"type":"string","title":"Chunk Id"},"content_piece_id":{"type":"string","title":"Content Piece Id"},"title":{"type":"string","title":"Title"},"excerpt":{"type":"string","title":"Excerpt"},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"},"source_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Type"},"lifecycle_state":{"type":"string","title":"Lifecycle State"},"embedded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Embedded At"},"embedding_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Embedding Model"},"last_source_updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Source Updated At"},"authority_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Authority Score"},"taxonomy_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Domain"},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name"},"author_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Email"},"department":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Department"}},"type":"object","required":["chunk_id","content_piece_id","title","excerpt","lifecycle_state"],"title":"SourceChunkSchema","description":"Per-chunk provenance entry for the MCP `get_source_detail` tool."},"SourceDetailResponse":{"properties":{"query_id":{"type":"string","title":"Query Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"chunks":{"items":{"$ref":"#/components/schemas/SourceChunkSchema"},"type":"array","title":"Chunks"},"chunk_count":{"type":"integer","title":"Chunk Count"}},"type":"object","required":["query_id","tenant_id","chunks","chunk_count"],"title":"SourceDetailResponse","description":"GET /v1/enterprise/sources/{query_id} response.\n\nReturns chunk-level provenance for a previously-executed query.\nLook up by `QueryLog.id` filtered by `tenant_id`; cross-tenant\naccess is indistinguishable from a missing row (404, not 403). Pre-\nv0.7 rows that lack `returned_chunk_ids` also 404."},"SourceStateResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"sync_status":{"type":"string","title":"Sync Status"},"paused_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Paused At"}},"type":"object","required":["connector_id","sync_status"],"title":"SourceStateResponse","description":"Shared response shape for Track I state-change endpoints\n(pause, resume — and v0.5.9+ disconnect will reuse)."},"SourceTreeItem":{"properties":{"source_id":{"type":"string","title":"Source Id","description":"enterprise_connectors.id (UUID)"},"connector_type":{"type":"string","title":"Connector Type","description":"google_drive | s3 | upload | ..."},"name":{"type":"string","title":"Name"},"primary_identifier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Primary Identifier"},"sync_status":{"type":"string","title":"Sync Status","description":"'active' | 'paused' — UI greys-out paused rows.","default":"active"},"piece_count":{"type":"integer","title":"Piece Count","description":"Visible pieces under this source. Equals the same count the Sources page shows.","default":0},"has_children":{"type":"boolean","title":"Has Children","description":"True iff piece_count > 0. Frontend hides expand chevron when False."}},"type":"object","required":["source_id","connector_type","name","has_children"],"title":"SourceTreeItem","description":"One source (connector) with enough context to render the tree row.\n\n``piece_count`` is the number of non-soft-deleted, non-removed pieces\nvisible in the tenant's library — the same predicate the Sources page\nuses for its file_count column."},"SourceTreeResponse":{"properties":{"sources":{"items":{"$ref":"#/components/schemas/SourceTreeItem"},"type":"array","title":"Sources"},"tenant_total_pieces":{"type":"integer","title":"Tenant Total Pieces","description":"Sum of piece_counts. Useful for rendering 'X of N selected' in the inversion-mode UX hint (v0.6+) and the W6 dropdown header."}},"type":"object","required":["sources","tenant_total_pieces"],"title":"SourceTreeResponse","description":"GET /v1/sources/tree response."},"SsoBridgeResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id","description":"UUID of the resolved tenant."},"member_id":{"type":"string","title":"Member Id","description":"UUID of the resolved tenant_member."},"email":{"type":"string","title":"Email","description":"Member's email from SSO claims."},"name":{"type":"string","title":"Name","description":"Display name from member record."},"role":{"type":"string","enum":["owner","admin","member"],"title":"Role","description":"Tenant role. Narrowed to (owner, admin, member) in v0.8.8 (alembic v088a002 collapsed manager + guest). The bridge never returns 'manager' or 'guest'."},"scope":{"type":"string","enum":["read","write"],"title":"Scope","description":"Ephemeral key scope. Always 'read' at v0.8.8.","default":"read"},"ephemeral_api_key":{"type":"string","title":"Ephemeral Api Key","description":"Full plaintext ephemeral API key. The Worker encrypts and stores it; never persists plaintext."},"ephemeral_api_key_id":{"type":"string","title":"Ephemeral Api Key Id","description":"UUID of the minted enterprise_api_keys row."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Wall-clock expiry of the ephemeral key."}},"type":"object","required":["tenant_id","member_id","email","name","role","ephemeral_api_key","ephemeral_api_key_id","expires_at"],"title":"SsoBridgeResponse","description":"Response from ``POST /v1/auth/sso-bridge`` for the MCP server.\n\nCalled by the MCP Worker's ``/oauth/callback`` after a user has\nauthenticated with Clerk. Returns the per-employee identity that the\nWorker persists alongside its OAuth tokens, plus a freshly minted\nephemeral API key the Worker uses for downstream backend calls. The\nephemeral key is shown ONCE — the Worker stores it encrypted in KV\nand discards the plaintext."},"SsoBypassRequest":{"properties":{"enabled":{"type":"boolean","title":"Enabled"}},"type":"object","required":["enabled"],"title":"SsoBypassRequest"},"StartSyncRequest":{"properties":{"confirmed_folders":{"items":{"type":"string"},"type":"array","maxItems":500,"title":"Confirmed Folders","description":"Final folder/channel/space selection from the picker. Empty when mode='everything'."},"confirmed_root_files":{"type":"boolean","title":"Confirmed Root Files","description":"Whether to include loose files at the source root. Forced True when mode='everything'.","default":false},"confirmation_token":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Confirmation Token","description":"Optional client-generated nonce that prevents accidental double-confirmation when the user clicks the modal twice. When supplied and equal to the previously-stored token the endpoint is a no-op."}},"additionalProperties":false,"type":"object","title":"StartSyncRequest","description":"POST /v1/enterprise/connectors/{id}/start-sync request body."},"StartSyncResponse":{"properties":{"sync_id":{"type":"string","title":"Sync Id"},"estimated_files":{"type":"integer","title":"Estimated Files"},"estimated_duration_minutes":{"type":"integer","title":"Estimated Duration Minutes"}},"type":"object","required":["sync_id","estimated_files","estimated_duration_minutes"],"title":"StartSyncResponse","description":"POST /v1/enterprise/connectors/{id}/start-sync response (202 Accepted)."},"StepUpRequest":{"properties":{"auth_method":{"type":"string","enum":["password","mfa"],"title":"Auth Method","description":"How the caller re-authenticated. 'password' = the IdP / SSO flow at first-touch (presenting a valid JWT counts). 'mfa' = the caller is claiming an MFA event; ``mfa_code`` MUST be supplied. Full server-side MFA verification is a follow-up workstream — at this iteration the value is recorded for audit-log forensics."},"mfa_code":{"anyOf":[{"type":"string","maxLength":16},{"type":"null"}],"title":"Mfa Code","description":"Operator-supplied MFA code. Required iff ``auth_method='mfa'``. Not validated server-side at this iteration; recorded in structured logs for forensics."}},"additionalProperties":false,"type":"object","required":["auth_method"],"title":"StepUpRequest","description":"``POST /v1/auth/step-up`` request body."},"StepUpResponse":{"properties":{"step_up_token":{"type":"string","title":"Step Up Token","description":"Opaque step-up token. Pass via the ``X-Step-Up-Token`` header on the follow-up sensitive call. Prefix: ``qlv_stepup_``."},"expires_at":{"type":"string","format":"date-time","title":"Expires At","description":"Absolute UTC timestamp at which this token will be rejected. Re-mint after this point — the server will not extend the TTL on a presented token."},"ttl_seconds":{"type":"integer","title":"Ttl Seconds","description":"Convenience field — seconds from issuance to expiry.","default":300},"auth_method":{"type":"string","enum":["password","mfa"],"title":"Auth Method","description":"Echo of the auth_method recorded on the binding."}},"additionalProperties":false,"type":"object","required":["step_up_token","expires_at","auth_method"],"title":"StepUpResponse","description":"``POST /v1/auth/step-up`` 200 response."},"SubmitFeedbackRequest":{"properties":{"query_id":{"type":"string","format":"uuid","title":"Query Id","description":"The query_logs.id this feedback rates."},"rating":{"type":"string","title":"Rating","description":"'thumbs_up' or 'thumbs_down'."},"reason_codes":{"items":{"type":"string"},"type":"array","title":"Reason Codes","description":"Codes from the reason taxonomy. Allowed only with thumbs_down (silently ignored with thumbs_up). Empty array allowed — user registered dissatisfaction without categorizing."},"free_text":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Free Text","description":"Optional free-form comment. PII-scrubbed at write time."},"missing_source_hint":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Missing Source Hint","description":"Where the user thinks the answer should have come from. Allowed only when 'missing_information' is in reason_codes."},"problematic_citation_indices":{"anyOf":[{"items":{"type":"integer"},"type":"array"},{"type":"null"}],"title":"Problematic Citation Indices","description":"1-indexed citation badges the user marked as problematic. Allowed only when 'source_doesnt_say_that' is in reason_codes. Range validated against the snapshot's chunk_ids_cited length."},"ui_bug_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ui Bug Category","description":"One of: formatting | citations | controls | other. Allowed only when 'ui_bug' is in reason_codes."},"synthesis_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Synthesis Model","description":"The synthesis LLM the frontend received the answer from. Server reconciles against its own resolution; both sides should agree on a healthy day."},"chunk_ids_cited":{"items":{"type":"string"},"type":"array","title":"Chunk Ids Cited","description":"The chunk IDs the LLM actually cited in this answer. The frontend already knows these (it renders [1], [2] badges) — day-1 capture is the only honest answer because the backend does not store synthesis answer text."},"chunk_metadata_snapshot":{"anyOf":[{"additionalProperties":{"type":"object"},"type":"object"},{"type":"null"}],"title":"Chunk Metadata Snapshot","description":"Per-cited-chunk metadata bundle (rank, score, authority_score, taxonomy_domain, is_superseded, relationships, heading_path, content_piece_id). Frontend assembles from existing source-card state; backend validates score/authority_score ∈ [0,1] and rank ≥ 1, then stores verbatim."},"answer_text_hash":{"type":"string","maxLength":64,"minLength":64,"pattern":"^[0-9a-f]{64}$","title":"Answer Text Hash","description":"SHA-256 of the synthesized answer text (lowercase hex). Server can recompute from its accumulated synthesis_text buffer; including it on the request defends against stale-tab races where the user is rating an answer that no longer matches what the server thinks they saw."}},"type":"object","required":["query_id","rating","answer_text_hash"],"title":"SubmitFeedbackRequest","description":"POST /v1/feedback request body.\n\nSee ``submit_feedback.SubmitFeedbackInput`` for field-level rationale."},"SubmitFeedbackResponse":{"properties":{"feedback_id":{"type":"string","format":"uuid","title":"Feedback Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"free_text_was_scrubbed":{"type":"boolean","title":"Free Text Was Scrubbed"}},"type":"object","required":["feedback_id","created_at","free_text_was_scrubbed"],"title":"SubmitFeedbackResponse","description":"POST /v1/feedback response body."},"SubmitSurveyRequest":{"properties":{"score_numeric":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Score Numeric"},"score_categorical":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Score Categorical"},"comment":{"anyOf":[{"type":"string","maxLength":4000},{"type":"null"}],"title":"Comment"},"trigger_event":{"type":"string","maxLength":50,"title":"Trigger Event"},"trigger_context":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Trigger Context"},"idempotency_key":{"type":"string","format":"uuid","title":"Idempotency Key"}},"type":"object","required":["trigger_event","idempotency_key"],"title":"SubmitSurveyRequest","description":"``POST /v1/surveys/{instrument}/submit`` body.\n\n``score_numeric`` for NPS/CSAT (numeric scales).\n``score_categorical`` for PMF/CES (categorical scales).\nExactly one must be provided — the action validates."},"SubmitSurveyResponse":{"properties":{"recorded":{"type":"boolean","title":"Recorded"},"record_id":{"type":"string","format":"uuid","title":"Record Id"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"duplicate":{"type":"boolean","title":"Duplicate","default":false}},"type":"object","required":["recorded","record_id","created_at"],"title":"SubmitSurveyResponse","description":"Always 200 on success (including dedup); 409 reserved for the\nexplicit duplicate-key error path the route maps from\n``result.duplicate=True``."},"SurveyFollowUpResponse":{"properties":{"default_prompt":{"type":"string","title":"Default Prompt"},"required":{"type":"boolean","title":"Required"},"conditional_on":{"additionalProperties":{"type":"string"},"type":"object","title":"Conditional On"}},"type":"object","required":["default_prompt","required"],"title":"SurveyFollowUpResponse","description":"Free-text follow-up prompt (conditional on score, if applicable)."},"SurveyInstrumentResponse":{"properties":{"instrument":{"type":"string","enum":["nps","pmf","csat","ces"],"title":"Instrument"},"question":{"type":"string","title":"Question"},"scale":{"$ref":"#/components/schemas/SurveyScaleResponse"},"follow_up":{"anyOf":[{"$ref":"#/components/schemas/SurveyFollowUpResponse"},{"type":"null"}]},"placement":{"type":"string","title":"Placement"}},"type":"object","required":["instrument","question","scale","placement"],"title":"SurveyInstrumentResponse","description":"Full instrument config returned to the frontend."},"SurveyOptionResponse":{"properties":{"value":{"type":"string","title":"Value"},"label":{"type":"string","title":"Label"}},"type":"object","required":["value","label"],"title":"SurveyOptionResponse","description":"One labelled choice on a categorical scale."},"SurveyOrchestratorMetricsResponse":{"properties":{"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"rows":{"items":{"$ref":"#/components/schemas/InstrumentMetricsRowResponse"},"type":"array","title":"Rows"}},"type":"object","required":["window_start","window_end","rows"],"title":"SurveyOrchestratorMetricsResponse"},"SurveyPreferencesResponse":{"properties":{"hard_opt_out":{"type":"boolean","title":"Hard Opt Out"}},"type":"object","required":["hard_opt_out"],"title":"SurveyPreferencesResponse","description":"Current per-user survey orchestrator preferences.\n\nPR-S2a surfaces only ``hard_opt_out`` — the user-facing \"Don't show\nme product surveys.\" toggle in settings. ``survey_averse_until`` is\nan orchestrator-internal state, not user-controlled."},"SurveyResponseRequest":{"properties":{"user_id":{"type":"string","minLength":1,"title":"User Id"},"persona":{"type":"string","pattern":"^enterprise$","title":"Persona"},"trigger_event":{"type":"string","maxLength":50,"minLength":1,"title":"Trigger Event"},"nps_score":{"type":"integer","maximum":10.0,"minimum":0.0,"title":"Nps Score"},"pmf_response":{"type":"string","pattern":"^(very_disappointed|somewhat_disappointed|not_disappointed)$","title":"Pmf Response"},"csat_score":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Csat Score"},"ces_score":{"type":"integer","maximum":5.0,"minimum":1.0,"title":"Ces Score"}},"type":"object","required":["user_id","persona","trigger_event","nps_score","pmf_response","csat_score","ces_score"],"title":"SurveyResponseRequest","description":"Request body for survey submission."},"SurveyResponseWithCommentRowResponse":{"properties":{"record_id":{"type":"string","format":"uuid","title":"Record Id"},"tenant_member_id":{"type":"string","format":"uuid","title":"Tenant Member Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"instrument":{"type":"string","title":"Instrument"},"score_numeric":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Score Numeric"},"score_categorical":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Score Categorical"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"trigger_event":{"type":"string","title":"Trigger Event"},"billing_state":{"type":"string","title":"Billing State"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["record_id","tenant_member_id","tenant_id","instrument","trigger_event","billing_state","created_at"],"title":"SurveyResponseWithCommentRowResponse","description":"One submission row for the backoffice qualitative-review surface."},"SurveyScaleResponse":{"properties":{"kind":{"type":"string","enum":["numeric","categorical"],"title":"Kind"},"numeric_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Numeric Min"},"numeric_max":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Numeric Max"},"options":{"items":{"$ref":"#/components/schemas/SurveyOptionResponse"},"type":"array","title":"Options"}},"type":"object","required":["kind"],"title":"SurveyScaleResponse","description":"Either a numeric range or a categorical option set."},"SuspiciousKeyItem":{"properties":{"api_key_id":{"type":"string","format":"uuid","title":"Api Key Id"},"query_count":{"type":"integer","title":"Query Count"},"error_rate":{"type":"number","title":"Error Rate"},"first_seen":{"type":"string","format":"date-time","title":"First Seen"},"last_seen":{"type":"string","format":"date-time","title":"Last Seen"},"signals":{"items":{"$ref":"#/components/schemas/SuspiciousSignalSchema"},"type":"array","title":"Signals"}},"type":"object","required":["api_key_id","query_count","error_rate","first_seen","last_seen","signals"],"title":"SuspiciousKeyItem"},"SuspiciousKeysResponse":{"properties":{"since":{"type":"string","format":"date-time","title":"Since"},"evaluated_keys":{"type":"integer","title":"Evaluated Keys"},"flagged_count":{"type":"integer","title":"Flagged Count"},"items":{"items":{"$ref":"#/components/schemas/SuspiciousKeyItem"},"type":"array","title":"Items"}},"type":"object","required":["since","evaluated_keys","flagged_count","items"],"title":"SuspiciousKeysResponse"},"SuspiciousSignalSchema":{"properties":{"name":{"type":"string","title":"Name"},"detail":{"type":"string","title":"Detail"},"severity":{"type":"string","title":"Severity"},"measured_value":{"type":"number","title":"Measured Value"},"threshold":{"type":"number","title":"Threshold"}},"type":"object","required":["name","detail","severity","measured_value","threshold"],"title":"SuspiciousSignalSchema"},"SyncFileIssue":{"properties":{"id":{"type":"string","title":"Id"},"connector_id":{"type":"string","title":"Connector Id"},"sync_job_id":{"type":"string","title":"Sync Job Id"},"sync_id":{"type":"string","title":"Sync Id"},"source_file_key":{"type":"string","title":"Source File Key"},"source_file_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source File Id"},"source_file_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source File Name"},"source_file_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source File Url"},"source_mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Mime Type"},"source_size_bytes":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Source Size Bytes"},"status":{"type":"string","enum":["skipped","failed"],"title":"Status"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Message"},"attempt_count":{"type":"integer","title":"Attempt Count"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"started_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Started At"},"finished_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Finished At"}},"type":"object","required":["id","connector_id","sync_job_id","sync_id","source_file_key","status","attempt_count","created_at"],"title":"SyncFileIssue","description":"A connector file that did not make it into the content library."},"SyncResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"files_processed":{"type":"integer","title":"Files Processed"},"files_skipped":{"type":"integer","title":"Files Skipped"},"files_errored":{"type":"integer","title":"Files Errored"},"errors":{"items":{"type":"string"},"type":"array","title":"Errors"},"status":{"type":"string","title":"Status"}},"type":"object","required":["connector_id","files_processed","files_skipped","files_errored","errors","status"],"title":"SyncResponse","description":"POST /v1/enterprise/connectors/{connector_id}/sync response."},"SyncStatusResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"state":{"type":"string","enum":["not_started","syncing","completed","failed"],"title":"State"},"files_processed":{"type":"integer","title":"Files Processed"},"total_estimated":{"type":"integer","title":"Total Estimated"},"last_sync_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Sync At"},"error_message":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Message"},"file_issues":{"items":{"$ref":"#/components/schemas/SyncFileIssue"},"type":"array","title":"File Issues"}},"type":"object","required":["connector_id","state","files_processed","total_estimated"],"title":"SyncStatusResponse","description":"GET /v1/enterprise/connectors/{id}/sync-status response."},"SynthesisDebugResponse":{"properties":{"query_log_id":{"type":"string","title":"Query Log Id"},"tenant_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Id"},"employee_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Id"},"query_hash":{"type":"string","title":"Query Hash"},"query_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Type"},"coverage_level":{"type":"string","title":"Coverage Level"},"mode":{"type":"string","title":"Mode"},"status":{"type":"string","title":"Status"},"retrieval_execution_state":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Retrieval Execution State"},"sources_returned":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Sources Returned"},"returned_chunk_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Returned Chunk Ids"},"input_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Input Tokens"},"output_tokens":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Output Tokens"},"taxonomy_domain":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Taxonomy Domain"},"created_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created At"},"is_synthesis_v2":{"type":"boolean","title":"Is Synthesis V2"},"signal_firing":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Signal Firing"},"grounding_check_results":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Grounding Check Results"}},"type":"object","required":["query_log_id","query_hash","coverage_level","mode","status","is_synthesis_v2"],"title":"SynthesisDebugResponse","description":"Synthesis Output Inspector payload for one query log id.\n\nNote: the raw query text is intentionally absent — query_logs stores\nonly ``query_hash`` (privacy). The firing/grounding slices are NULL when\nthe legacy synthesis path served the query."},"SynthesisPipelineResponse":{"properties":{"query_count_24h":{"type":"integer","title":"Query Count 24H"},"aborted_rate":{"type":"number","title":"Aborted Rate"},"error_rate":{"type":"number","title":"Error Rate"},"mode_breakdown":{"additionalProperties":{"type":"integer"},"type":"object","title":"Mode Breakdown"},"hourly_query_rate":{"items":{"type":"integer"},"type":"array","title":"Hourly Query Rate"},"latency_p50_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency P50 Seconds"},"latency_p95_seconds":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Latency P95 Seconds"},"citation_accuracy_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Citation Accuracy Pct"}},"type":"object","required":["query_count_24h","aborted_rate","error_rate","mode_breakdown","hourly_query_rate"],"title":"SynthesisPipelineResponse"},"SynthesisPromptsResponse":{"properties":{"variants":{"items":{"$ref":"#/components/schemas/PromptVariantResponse"},"type":"array","title":"Variants"},"shared_core_identical":{"type":"boolean","title":"Shared Core Identical"},"shared_core_sha256":{"type":"string","title":"Shared Core Sha256"},"notes":{"items":{"type":"string"},"type":"array","title":"Notes"}},"type":"object","required":["variants","shared_core_identical","shared_core_sha256"],"title":"SynthesisPromptsResponse"},"TeamsAuthUrlResponse":{"properties":{"auth_url":{"type":"string","title":"Auth Url"}},"type":"object","required":["auth_url"],"title":"TeamsAuthUrlResponse","description":"GET /v1/enterprise/connect/teams response."},"TeamsChannelItem":{"properties":{"team_id":{"type":"string","title":"Team Id"},"team_name":{"type":"string","title":"Team Name"},"channel_id":{"type":"string","title":"Channel Id"},"channel_name":{"type":"string","title":"Channel Name"},"membership_type":{"type":"string","title":"Membership Type","default":"standard"},"description":{"type":"string","title":"Description","default":""}},"type":"object","required":["team_id","team_name","channel_id","channel_name"],"title":"TeamsChannelItem","description":"One channel in a Team, surfaced by the picker."},"TeamsChannelsResponse":{"properties":{"channels":{"items":{"$ref":"#/components/schemas/TeamsChannelItem"},"type":"array","title":"Channels"},"selected_channel_keys":{"items":{"type":"string"},"type":"array","title":"Selected Channel Keys"},"message_score_threshold":{"type":"integer","title":"Message Score Threshold"}},"type":"object","required":["channels","message_score_threshold"],"title":"TeamsChannelsResponse","description":"GET /v1/enterprise/connectors/{id}/teams/channels response."},"TeamsScopeRequest":{"properties":{"channel_keys":{"items":{"type":"string"},"type":"array","maxItems":500,"title":"Channel Keys"},"message_score_threshold":{"anyOf":[{"type":"integer","maximum":30.0,"minimum":1.0},{"type":"null"}],"title":"Message Score Threshold","description":"Override the score threshold for high-signal message indexing (default 5). Higher = less noise, lower = more coverage."}},"type":"object","title":"TeamsScopeRequest","description":"POST /v1/enterprise/connectors/{id}/teams/scope body.\n\n``channel_keys`` are composite identifiers of the form\n``{team_id}:{channel_id}`` so one scope array can carry channels\nfrom multiple teams without a nested structure."},"TeamsScopeResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"channel_keys":{"items":{"type":"string"},"type":"array","title":"Channel Keys"},"channel_count":{"type":"integer","title":"Channel Count"},"message_score_threshold":{"type":"integer","title":"Message Score Threshold"}},"type":"object","required":["connector_id","channel_keys","channel_count","message_score_threshold"],"title":"TeamsScopeResponse","description":"POST /v1/enterprise/connectors/{id}/teams/scope response."},"TelemetryAcceptedResponse":{"properties":{"accepted":{"type":"boolean","title":"Accepted","default":true},"event_id":{"type":"integer","title":"Event Id"}},"type":"object","required":["event_id"],"title":"TelemetryAcceptedResponse"},"TelemetryEventBody":{"properties":{"event_kind":{"type":"string","enum":["cli_crash","cli_command_completed","cli_command_failed"],"title":"Event Kind"},"cli_version":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Cli Version"},"os_platform":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Os Platform"},"os_release":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Os Release"},"node_version":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Node Version"},"command_name":{"anyOf":[{"type":"string","maxLength":64},{"type":"null"}],"title":"Command Name"},"error_class":{"anyOf":[{"type":"string","maxLength":128},{"type":"null"}],"title":"Error Class"},"error_message_hash":{"anyOf":[{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},{"type":"null"}],"title":"Error Message Hash"},"duration_ms":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Duration Ms"},"exit_code":{"anyOf":[{"type":"integer","maximum":255.0,"minimum":-1.0},{"type":"null"}],"title":"Exit Code"}},"additionalProperties":false,"type":"object","required":["event_kind"],"title":"TelemetryEventBody","description":"CLI telemetry event payload. Never carries raw error messages.\n\n``error_message_hash`` is a SHA-256 hex digest (64 chars) computed\nclient-side; the route stores it as-is. There is intentionally no\n``error_message`` field — see module docstring."},"TelemetrySummaryResponse":{"properties":{"window_days":{"type":"integer","title":"Window Days"},"total_events":{"type":"integer","title":"Total Events"},"versions":{"items":{"type":"object"},"type":"array","title":"Versions"},"commands":{"items":{"type":"object"},"type":"array","title":"Commands"},"error_classes":{"items":{"type":"object"},"type":"array","title":"Error Classes"}},"type":"object","required":["window_days","total_events","versions","commands","error_classes"],"title":"TelemetrySummaryResponse","description":"Aggregate stats for the admin telemetry dashboard.\n\nAll counts are over the last ``window_days`` (default 30) days\nwithin the calling admin's tenant."},"TenantBackfillConnectorResponse":{"properties":{"connector_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Connector Id","description":"``enterprise_connectors.id`` (str UUID) — NULL for ``connector_type='upload'``, which has no connectors row."},"connector_type":{"type":"string","title":"Connector Type","description":"Connector type slug (``google_drive``, ``slack``, ``upload``, etc.). ``upload`` is the synthetic bucket for pieces with ``connector_id IS NULL``."},"status":{"type":"string","enum":["completed","in_progress"],"title":"Status","description":"Derived from the per-connector pieces split — ``completed`` when every piece is backfilled, else ``in_progress``."},"pieces_total":{"type":"integer","title":"Pieces Total"},"pieces_backfilled":{"type":"integer","title":"Pieces Backfilled"},"pieces_pending":{"type":"integer","title":"Pieces Pending"}},"type":"object","required":["connector_type","status","pieces_total","pieces_backfilled","pieces_pending"],"title":"TenantBackfillConnectorResponse","description":"Per-connector roll-up for the caller's tenant.\n\nShape mirrors the operator variant (``PermissionBackfillConnectorResponse``)\nplus an explicit ``status`` field. ``status`` is derived from the\npieces split — ``completed`` when ``pieces_pending == 0``, else\n``in_progress``. The frontend uses it to colour rows without having\nto do arithmetic on render."},"TenantBackfillTriggerRequest":{"properties":{"force":{"type":"boolean","title":"Force","description":"When True, restart the backfill even if a 'completed' progress row exists for this tenant. Default False — the trigger is a no-op when the tenant has already drained.","default":false},"max_pieces_per_batch":{"anyOf":[{"type":"integer","maximum":10000.0,"minimum":1.0},{"type":"null"}],"title":"Max Pieces Per Batch","description":"Override ``Settings.permission_backfill_batch_size`` for this invocation. Bounded [1, 10_000] — operators canary with small values on large tenants before scaling up."}},"type":"object","title":"TenantBackfillTriggerRequest","description":"Optional tunables for the per-tenant manual backfill trigger.\n\nThe Sprint 3 canary path: the operator POSTs an empty body for a\nroutine kickoff, or supplies ``force=True`` to restart a tenant\nwhose ``permission_backfill_progress`` row is already ``completed``\n(rare — used when a runner bug requires a rerun under a fixed\nbinary). ``max_pieces_per_batch`` overrides\n``Settings.permission_backfill_batch_size`` for this invocation\nonly — useful for a deliberately small canary batch on a large\ntenant before unleashing the full nightly batch."},"TenantBackfillTriggerResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"started_at":{"type":"string","format":"date-time","title":"Started At"},"completed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Completed At"},"pieces_processed":{"type":"integer","title":"Pieces Processed"},"pieces_unresolved":{"type":"integer","title":"Pieces Unresolved"},"pieces_qdrant_updated":{"type":"integer","title":"Pieces Qdrant Updated"},"pieces_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pieces Total"},"last_processed_piece_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Processed Piece Id"},"last_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Error"},"last_error_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Error At"},"tenant_completed":{"type":"boolean","title":"Tenant Completed"},"tenant_capped":{"type":"boolean","title":"Tenant Capped"},"killed":{"type":"boolean","title":"Killed"},"failure_budget_exceeded":{"type":"boolean","title":"Failure Budget Exceeded"},"triggered_by":{"type":"string","title":"Triggered By"},"triggered_at":{"type":"string","format":"date-time","title":"Triggered At"}},"type":"object","required":["tenant_id","tenant_name","started_at","pieces_processed","pieces_unresolved","pieces_qdrant_updated","tenant_completed","tenant_capped","killed","failure_budget_exceeded","triggered_by","triggered_at"],"title":"TenantBackfillTriggerResponse","description":"Result of a manual backfill trigger.\n\nShape mirrors :class:`PermissionBackfillTenantDetailResponse` for the\nfields the operator dashboard cares about (progress + counters),\nextended with ``triggered_by`` / ``triggered_at`` so the kickoff is\nobservable separately from the per-piece audit-log rows the runner\nitself writes (``mutation_source='backfill'``)."},"TenantMemoryDumpResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"tenant_name":{"type":"string","title":"Tenant Name"},"long_term_memory":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Long Term Memory"},"short_term_memory":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Short Term Memory"},"last_edit_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edit At"},"last_edit_applied_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edit Applied By"},"last_edit_artifact":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Edit Artifact"}},"type":"object","required":["tenant_id","tenant_name","long_term_memory","short_term_memory","last_edit_at","last_edit_applied_by","last_edit_artifact"],"title":"TenantMemoryDumpResponse","description":"Operator view of a single tenant's brain memory state.\n\nDiffers from the user-self transparency slice\n(``GetMemoryResponse.tenant_memory``) by exposing the raw JSONB\npayloads end-to-end. Raw confidence scores are intentionally surfaced\n— operators need them to debug classifier behaviour. The privacy\nbands live only in the user-self surface."},"TenantResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email_domain":{"type":"string","title":"Email Domain"},"sso_provider":{"type":"string","title":"Sso Provider"},"status":{"type":"string","title":"Status"},"trial_tokens":{"type":"integer","title":"Trial Tokens"},"tokens_used":{"type":"integer","title":"Tokens Used"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","name","email_domain","sso_provider","status","trial_tokens","tokens_used","created_at"],"title":"TenantResponse","description":"Tenant in API responses."},"TenantUsageResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"period_start":{"type":"string","format":"date","title":"Period Start"},"period_end":{"type":"string","format":"date","title":"Period End"},"total_queries":{"type":"integer","title":"Total Queries"},"total_input_tokens":{"type":"integer","title":"Total Input Tokens"},"total_output_tokens":{"type":"integer","title":"Total Output Tokens"},"provider_cost_usd_micros":{"type":"integer","title":"Provider Cost Usd Micros"},"customer_kt_total":{"type":"integer","title":"Customer Kt Total"},"daily":{"items":{"$ref":"#/components/schemas/DailyMetricResponse"},"type":"array","title":"Daily"},"top_models_by_cost":{"items":{"$ref":"#/components/schemas/TopEntityResponse"},"type":"array","title":"Top Models By Cost"}},"type":"object","required":["tenant_id","period_start","period_end","total_queries","total_input_tokens","total_output_tokens","provider_cost_usd_micros","customer_kt_total","daily","top_models_by_cost"],"title":"TenantUsageResponse"},"TenantUsersResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/UserMemorySummaryResponse"},"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":"TenantUsersResponse"},"ThirdPartyEntryView":{"properties":{"staleness_days":{"type":"integer","title":"Staleness Days","description":"Days since ``verified_at``. Catalog viewer renders green ≤90, yellow 91-180, red >180. Computed server-side so xlsx export carries the same value the live viewer displays."},"provider":{"type":"string","title":"Provider"},"service":{"type":"string","title":"Service"},"pricing":{"$ref":"#/components/schemas/Pricing"},"source_url":{"type":"string","title":"Source Url","description":"Provider pricing page URL — opens in the backoffice 'verify now' button."},"verified_at":{"type":"string","format":"date","title":"Verified At","description":"Last date a human checked the rate against the provider page. Backoffice flags >90 days as stale."},"call_sites":{"items":{"type":"string"},"type":"array","title":"Call Sites","description":"file:line pointers to where the cost is incurred. Optional for infra entries (provisioned-not-called)."},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Free-text caveats / migration history."},"billing_model":{"type":"string","enum":["per_seat","per_event","per_request","per_token","per_email","per_transaction","flat_monthly","tiered_monthly","percent_of_revenue","free_tier"],"title":"Billing Model"}},"type":"object","required":["staleness_days","provider","service","pricing","source_url","verified_at","billing_model"],"title":"ThirdPartyEntryView","description":"Third-party entry enriched with ``staleness_days``."},"ThreadCreatedResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"is_pinned":{"type":"boolean","title":"Is Pinned"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","title","is_pinned","created_at"],"title":"ThreadCreatedResponse","description":"POST /v1/enterprise/threads response."},"ThreadDetailResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"is_pinned":{"type":"boolean","title":"Is Pinned"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_query_at":{"type":"string","format":"date-time","title":"Last Query At"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"},"messages":{"items":{"$ref":"#/components/schemas/MessageResponse"},"type":"array","title":"Messages"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor"}},"type":"object","required":["id","title","is_pinned","created_at","last_query_at","expires_at","messages"],"title":"ThreadDetailResponse","description":"GET /v1/enterprise/threads/{id} response.\n\nCarries the full thread metadata + all messages in chronological\norder. ``next_cursor`` is non-null when more messages remain\nbeyond the requested page (rare — most threads have ≤ 50 turns)."},"ThreadSummaryResponse":{"properties":{"id":{"type":"string","title":"Id"},"title":{"type":"string","title":"Title"},"last_query_at":{"type":"string","format":"date-time","title":"Last Query At"},"is_pinned":{"type":"boolean","title":"Is Pinned"},"bucket":{"type":"string","enum":["today","yesterday","last_7d","last_30d"],"title":"Bucket"}},"type":"object","required":["id","title","last_query_at","is_pinned","bucket"],"title":"ThreadSummaryResponse","description":"One row in the sidebar list response.\n\n``bucket`` is server-computed so different timezones don't show\ndifferent groupings."},"TimelineNode":{"properties":{"chunk_id":{"type":"string","title":"Chunk Id"},"content_excerpt":{"type":"string","title":"Content Excerpt","description":"150-word windowed excerpt, same pattern as ChunkResult.excerpt."},"published_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Published At","description":"Effective publication timestamp resolved via fallback chain: (1) enterprise_content_pieces.publish_date, (2) enterprise_content_pieces.source_updated_at, (3) enterprise_content_pieces.created_at, (4) chunks.created_at. Null only when every fallback is null."},"edge_to_previous":{"anyOf":[{"$ref":"#/components/schemas/EdgeToPreviousRef"},{"type":"null"}],"description":"Cross-reference edge linking this row to its immediate predecessor in the chronological list. Null on row 0 or when no direct edge exists between the two rows."},"source_title":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Title","description":"Title of the chunk's parent content piece."},"author_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Author Name","description":"Display name of the content author. Null when the source does not expose author metadata."},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url","description":"Link back to the original source. For connector content (Drive, Slack, Notion, SharePoint, etc.) this is the upstream document URL stored on ``enterprise_content_pieces.source_url``. For direct uploads the same column carries the S3-backed URL when the bucket exposes one. Nullable — some content pieces have no resolvable URL."},"source_type":{"type":"string","title":"Source Type","description":"Connector slug for the chunk's parent content piece — the same string stored on ``enterprise_connectors.connector_type`` ('google_drive' / 'slack' / 'notion' / 'sharepoint' / 'confluence' / 'teams' / 'box' / 'dropbox' / 'gmail' / 's3' / 'upload'). Direct-upload pieces (no connector row) resolve to 'upload'. 'unknown' is the defensive fallback when the metadata join fails to resolve a row.","default":"unknown"}},"type":"object","required":["chunk_id","content_excerpt"],"title":"TimelineNode","description":"One chunk in the K2 timeline.\n\nv2 (2026-06-02): the timeline is a chronological list of the\ntopic's top-ranked chunks. Cross-reference edges are surfaced as\nper-row highlights via ``edge_to_previous`` — they no longer gate\nwhether the row renders at all.\n\nSort order: oldest → newest using\n``enterprise_content_pieces.publish_date`` →\n``source_updated_at`` → ``created_at`` → ``chunks.created_at``,\nwith ties broken on ``chunk_id`` for determinism.\n\n``edge_to_previous`` is ``None`` when:\n  * The row is the first node (no predecessor), OR\n  * No direct cross-reference edge connects this chunk to the\n    immediately preceding chronological row.\n\n``source_url`` + ``source_type`` (added 2026-06-03) power the\nfrontend's \"Open source\" link on each row:\n  * For connector-synced content, ``source_url`` is the original\n    document URL (Drive doc URL, Slack message link, Notion page,\n    SharePoint item URL, etc.) and ``source_type`` is the\n    connector slug (\"google_drive\" / \"slack\" / \"notion\" / …).\n  * For direct uploads (no connector), ``source_url`` is the\n    AWS-stored document URL (or ``None`` when the bucket exposes\n    no public/permanent link for the piece) and ``source_type``\n    is ``\"upload\"``."},"TimelineResponse":{"properties":{"topic":{"type":"string","title":"Topic","description":"The normalized topic query string."},"total_nodes":{"type":"integer","minimum":0.0,"title":"Total Nodes","description":"Count of nodes in the returned timeline. 0 only when the topic produced no hybrid_search hits."},"timeline":{"items":{"$ref":"#/components/schemas/TimelineNode"},"type":"array","title":"Timeline","description":"Chronological list of nodes, oldest first."}},"type":"object","required":["topic","total_nodes"],"title":"TimelineResponse","description":"GET /v1/enterprise/intelligence/timeline response body.\n\nv2 (2026-06-02): returns one row per top-ranked chunk for the\ntopic, sorted oldest → newest. Empty ``timeline`` (``total_nodes\n= 0``) only when the topic produced zero hybrid_search hits (or\none of the earlier short-circuits fired — empty topic, missing\ntenant, embedding failure). The frontend renders its empty state\nin that single case."},"TokenBalanceResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"trial_tokens":{"type":"integer","title":"Trial Tokens"},"tokens_used":{"type":"integer","title":"Tokens Used"},"tokens_remaining":{"type":"integer","title":"Tokens Remaining"},"is_trial":{"type":"boolean","title":"Is Trial"},"trial_days_remaining":{"type":"integer","title":"Trial Days Remaining"},"is_exhausted":{"type":"boolean","title":"Is Exhausted"}},"type":"object","required":["tenant_id","trial_tokens","tokens_used","tokens_remaining","is_trial","trial_days_remaining","is_exhausted"],"title":"TokenBalanceResponse","description":"GET /v1/enterprise/billing/balance response."},"TokenResponse":{"properties":{"access_token":{"type":"string","title":"Access Token"},"refresh_token":{"type":"string","title":"Refresh Token"},"token_type":{"type":"string","title":"Token Type","default":"Bearer"},"expires_in":{"type":"integer","title":"Expires In"}},"type":"object","required":["access_token","refresh_token","expires_in"],"title":"TokenResponse"},"TopEntityResponse":{"properties":{"label":{"type":"string","title":"Label","description":"tenant_id (UUID) or model name"},"query_count":{"type":"integer","title":"Query Count"},"provider_cost_usd_micros":{"type":"integer","title":"Provider Cost Usd Micros"},"tenant_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tenant Name"}},"type":"object","required":["label","query_count","provider_cost_usd_micros"],"title":"TopEntityResponse"},"TransferPlanSchema":{"properties":{"id":{"type":"string","title":"Id"},"domain":{"type":"string","title":"Domain"},"from_expert_email":{"type":"string","title":"From Expert Email"},"to_expert_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"To Expert Email"},"closest_alternate_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Closest Alternate Email"},"taxonomy_overlap_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Taxonomy Overlap Pct"},"status":{"type":"string","title":"Status"},"suggested_actions":{"items":{"type":"string"},"type":"array","title":"Suggested Actions"}},"type":"object","required":["id","domain","from_expert_email","status"],"title":"TransferPlanSchema","description":"Auto-generated transfer plan for one at-risk domain."},"TransitionPreviewResponse":{"properties":{"status":{"type":"string","title":"Status"},"from_plan_tier":{"type":"string","title":"From Plan Tier"},"to_plan_tier":{"type":"string","title":"To Plan Tier"},"from_seat_count":{"type":"integer","title":"From Seat Count"},"to_seat_count":{"type":"integer","title":"To Seat Count"},"effective_at":{"type":"string","format":"date-time","title":"Effective At"},"proration_total_usd":{"anyOf":[{"type":"string","pattern":"^-?\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Proration Total Usd"},"subtotal_usd":{"anyOf":[{"type":"string","pattern":"^\\d+(\\.\\d{2})?$"},{"type":"null"}],"title":"Subtotal Usd"},"line_items":{"items":{"$ref":"#/components/schemas/PreviewLineItem"},"type":"array","title":"Line Items"},"estimated_pack_refund_usd":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","title":"Estimated Pack Refund Usd","default":"0.00"},"estimated_pool_reset_kt":{"type":"integer","title":"Estimated Pool Reset Kt","default":0},"flapping_active":{"type":"boolean","title":"Flapping Active"},"flap_prior_attempts":{"type":"integer","title":"Flap Prior Attempts"},"existing_scheduled_change_id":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Existing Scheduled Change Id"},"existing_scheduled_target":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Scheduled Target"},"blocker":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Blocker"},"corpus_warning":{"anyOf":[{"type":"object"},{"type":"null"}],"title":"Corpus Warning"}},"type":"object","required":["status","from_plan_tier","to_plan_tier","from_seat_count","to_seat_count","effective_at","flapping_active","flap_prior_attempts"],"title":"TransitionPreviewResponse","description":"Response shape for GET /api/billing/transitions/preview.\n\nT11 plan-change modal consumes this verbatim — every field is\ntyped to match the action's TransitionPreviewOutcome dataclass."},"TransitionStatusRequest":{"properties":{"to_status":{"type":"string","pattern":"^(active|cancelled|deleted|free|payment_pending|pro_trial|suspended)$","title":"To Status","description":"Target status. Must be one of the 7 valid tenant states; the (from, to) pair must also be a valid transition or the primitive returns 400."},"reason":{"type":"string","maxLength":60,"minLength":1,"title":"Reason","description":"Operator justification. Stored on tenant_status_history.reason (≤60 chars) and the audit log row."},"expected_from_status":{"anyOf":[{"type":"string","pattern":"^(active|cancelled|deleted|free|payment_pending|pro_trial|suspended)$"},{"type":"null"}],"title":"Expected From Status","description":"Optional optimistic-concurrency guard. Operator declares the status they expect the tenant to currently be in. If the tenant has moved (e.g., a webhook fired between the operator's read and write), the request returns 409 without mutating state. Omit to skip the explicit check (the primitive's FOR UPDATE lock still serialises concurrent transitions)."}},"type":"object","required":["to_status","reason"],"title":"TransitionStatusRequest","description":"Body for POST /admin/tenants/{tenant_id}/transition-status."},"TransitionStatusResponse":{"properties":{"tenant_id":{"type":"string","title":"Tenant Id"},"from_status":{"type":"string","title":"From Status"},"to_status":{"type":"string","title":"To Status"},"reason":{"type":"string","title":"Reason"},"history_id":{"type":"string","title":"History Id"},"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"}},"type":"object","required":["tenant_id","from_status","to_status","reason","history_id","occurred_at"],"title":"TransitionStatusResponse","description":"Result of a successful state transition."},"TreePieceItem":{"properties":{"piece_id":{"type":"string","title":"Piece Id","description":"enterprise_content_pieces.id (UUID)"},"title":{"type":"string","title":"Title"},"mime_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Mime Type"},"source_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Url"},"updated_at":{"type":"string","format":"date-time","title":"Updated At","description":"EnterpriseContentPiece.updated_at — used as the keyset cursor. Pieces with no upstream timestamp still order by their Quelvio-side updated_at."}},"type":"object","required":["piece_id","title","updated_at"],"title":"TreePieceItem","description":"One piece (file) under a source. Returned by the lazy /pieces endpoint."},"TreePiecesResponse":{"properties":{"pieces":{"items":{"$ref":"#/components/schemas/TreePieceItem"},"type":"array","title":"Pieces"},"next_cursor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Next Cursor","description":"Opaque cursor — pass back as ``?cursor=`` to fetch the next page. Null when there are no more pieces."},"total":{"type":"integer","title":"Total","description":"Total piece count under this source matching the q filter (or without filter when q is empty). Echoed for the dropdown chip."}},"type":"object","required":["pieces","total"],"title":"TreePiecesResponse","description":"GET /v1/sources/tree/{source_id}/pieces response."},"UiBugQueueResponse":{"properties":{"rows":{"items":{"$ref":"#/components/schemas/UiBugRowResponse"},"type":"array","title":"Rows"},"total_count":{"type":"integer","title":"Total Count"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"}},"type":"object","required":["rows","total_count","limit","offset"],"title":"UiBugQueueResponse"},"UiBugRowResponse":{"properties":{"feedback_id":{"type":"string","format":"uuid","title":"Feedback Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"query_id":{"type":"string","format":"uuid","title":"Query Id"},"ui_bug_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Ui Bug Category"},"free_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Free Text"},"free_text_pii_scrubbed":{"type":"boolean","title":"Free Text Pii Scrubbed"},"answer_text_hash":{"type":"string","title":"Answer Text Hash"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["feedback_id","tenant_id","query_id","ui_bug_category","free_text","free_text_pii_scrubbed","answer_text_hash","created_at"],"title":"UiBugRowResponse"},"UnansweredQuerySchema":{"properties":{"query_hash":{"type":"string","title":"Query Hash"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"coverage_level":{"type":"string","title":"Coverage Level"},"query_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Query Type"}},"type":"object","required":["query_hash","created_at","coverage_level"],"title":"UnansweredQuerySchema","description":"One unanswered query for the Knowledge Map drill-down panel.\n\n``query_hash`` is the SHA-256 of the normalised query text — not the\ntext itself. The frontend uses ``created_at`` for the \"asked X days ago\"\nlabel and treats ``query_hash`` as the row key only (NOT displayed)."},"UnfurlRequest":{"properties":{"url":{"type":"string","title":"Url"}},"type":"object","required":["url"],"title":"UnfurlRequest","description":"Notion's unfurl callback payload (minimum viable shape).\n\nNotion sends additional fields (user id, workspace id, etc.) but\nat v0.5.14 we only use the URL — the answer snapshot is the full\npayload, and user identity is the Q4 documented limitation."},"UnresolvedVerdictListResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/UnresolvedVerdictResponse"},"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":"UnresolvedVerdictListResponse"},"UnresolvedVerdictResponse":{"properties":{"classifier_type":{"type":"string","title":"Classifier Type"},"source_id":{"type":"string","format":"uuid","title":"Source Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"classifier_verdict":{"type":"string","title":"Classifier Verdict"},"classifier_confidence":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Classifier Confidence"},"classifier_reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Classifier Reasoning"},"classified_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Classified At"},"source_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Label"}},"type":"object","required":["classifier_type","source_id","tenant_id","classifier_verdict","classifier_confidence","classifier_reasoning","classified_at","source_label"],"title":"UnresolvedVerdictResponse","description":"One unresolved classifier verdict awaiting operator review.\n\nUniform shape across both classifier_type values so the UI can\nrender a single table. ``source_id`` is the foreign key the operator\nPOSTs back to ``/classifier-feedback`` to submit a vote — either a\n``content_pieces.id`` (tenant_memory_significance) or a\n``user_event_significance.id`` (user_event_significance)."},"UpdateAssumptionRequest":{"properties":{"value":{"title":"Value","description":"New value for the slider. Type must match the row's ``value_type``; the route validates and rejects mismatches with 422."}},"type":"object","required":["value"],"title":"UpdateAssumptionRequest","description":"Body of ``PATCH /v1/admin/cost-catalog/assumptions/{key}``.\n\nOnly the ``value`` is editable — the slider min/max/step + the\ndescription are pinned in the DB seed and intentionally not\nruntime-mutable (changing the slider bounds requires a migration\nso the change is reviewable in git)."},"UpdateFilterPresetRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":80,"minLength":1},{"type":"null"}],"title":"Name"},"mode":{"anyOf":[{"type":"string","enum":["include","exclude"]},{"type":"null"}],"title":"Mode"},"source_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Source Ids"},"piece_ids":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Piece Ids"}},"type":"object","title":"UpdateFilterPresetRequest","description":"PATCH /v1/enterprise/search/filter-presets/{id} body.\n\nAll fields optional — only present ones are applied. Scope changes\ndo NOT go through here; use /publish or /unpublish instead."},"UpdateFoldersRequest":{"properties":{"folder_ids":{"items":{"type":"string"},"type":"array","maxItems":200,"title":"Folder Ids"}},"type":"object","title":"UpdateFoldersRequest","description":"POST /v1/enterprise/connectors/{connector_id}/folders request body.\n\nEmpty ``folder_ids`` is a valid request: an admin who deselects every\nfolder in the picker is intentionally choosing to sync nothing. The\nroute persists the empty list and does not trigger a sync.\n\n``max_length=200`` matches Box / Dropbox / Notion / Confluence per\nconnector parity (v0.5.22 Phase 1 — Pass 2 GDrive cell 2 secondary).\nPre-Phase-1 the cap was 50; users picking >50 folders saw an\nunactionable 422 because the FE adapter posts whatever the user\nselected with no client-side cap. With the new recursive sync\n(CC-2 / FIX-005) most users need fewer top-level selections, but\nbumping the cap removes a remaining sharp edge for orgs with broad\nDrive structures."},"UpdateFoldersResponse":{"properties":{"connector_id":{"type":"string","title":"Connector Id"},"folder_ids":{"items":{"type":"string"},"type":"array","title":"Folder Ids"},"folder_count":{"type":"integer","title":"Folder Count"}},"type":"object","required":["connector_id","folder_ids","folder_count"],"title":"UpdateFoldersResponse","description":"POST /v1/enterprise/connectors/{connector_id}/folders response."},"UpdateSecurityPolicyRequest":{"properties":{"max_oauth_session_age_hours":{"anyOf":[{"type":"integer","maximum":8760.0,"minimum":1.0},{"type":"null"}],"title":"Max Oauth Session Age Hours","description":"Hours. NULL or omitted = no enforcement. Range: 1..8760 (one year)."},"ip_allowlist_cidrs":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Ip Allowlist Cidrs","description":"List of CIDR ranges. NULL or omitted = no enforcement. Empty list is rejected — use NULL to disable."},"max_pats_per_user":{"anyOf":[{"type":"integer","maximum":100.0,"minimum":1.0},{"type":"null"}],"title":"Max Pats Per User","description":"NULL or omitted = no enforcement. Range: 1..100."},"max_service_accounts_per_tenant":{"anyOf":[{"type":"integer","maximum":1000.0,"minimum":1.0},{"type":"null"}],"title":"Max Service Accounts Per Tenant","description":"NULL or omitted = no enforcement. Range: 1..1000."}},"additionalProperties":false,"type":"object","title":"UpdateSecurityPolicyRequest","description":"PUT body for the admin security-policies endpoint.\n\nEvery field is independently nullable. ``None`` semantics differ\nbetween the column types:\n\n* Integer policies: ``None`` disables enforcement.\n* ``ip_allowlist_cidrs``: ``None`` disables enforcement; an empty\n  list is rejected by the route (use ``None`` to disable). This\n  avoids the \"deny-all by accident\" pitfall.\n\nAdmin must include their own IP address in ``ip_allowlist_cidrs``\nwhen setting a non-NULL list — the route checks the request's\n``X-Forwarded-For`` against the proposed list and returns 422 if\nthe admin would be locked out of the next request."},"UpdateServiceAccountRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":64,"minLength":1},{"type":"null"}],"title":"Name"},"description":{"anyOf":[{"type":"string","maxLength":1024},{"type":"null"}],"title":"Description"},"scopes":{"anyOf":[{"items":{"$ref":"#/components/schemas/ScopeEntry"},"type":"array"},{"type":"null"}],"title":"Scopes"}},"additionalProperties":false,"type":"object","title":"UpdateServiceAccountRequest"},"UpdateSurveyPreferencesRequest":{"properties":{"hard_opt_out":{"type":"boolean","title":"Hard Opt Out"}},"type":"object","required":["hard_opt_out"],"title":"UpdateSurveyPreferencesRequest","description":"Body for the opt-out toggle PUT."},"UpdateThreadRequest":{"properties":{"title":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Title"},"is_pinned":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Pinned"}},"type":"object","title":"UpdateThreadRequest","description":"PATCH /v1/enterprise/threads/{id} request body.\n\nBoth fields optional. At least one MUST be provided — validated in\nthe route handler before invoking the action."},"UpdateWebhookRequest":{"properties":{"enabled":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Enabled"},"rotate_secret":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Rotate Secret"},"new_secret":{"anyOf":[{"type":"string","maxLength":512,"minLength":16},{"type":"null"}],"title":"New Secret"}},"type":"object","title":"UpdateWebhookRequest"},"UsageOperationBreakdown":{"properties":{"operation_type":{"type":"string","title":"Operation Type","description":"Knowledge Token operation_type (KTR §6 retrieval class, KTR §5 ingestion class, KTR §7 index_daily). Customer dashboards typically render by family (Ingestion / Index / Retrieval)."},"kt":{"type":"integer","title":"Kt","description":"Sum of computed_kt for this operation_type"}},"type":"object","required":["operation_type","kt"],"title":"UsageOperationBreakdown","description":"One operation-type bucket inside the kT summary."},"UsageQueriesResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"period_start":{"type":"string","format":"date","title":"Period Start"},"period_end":{"type":"string","format":"date","title":"Period End"},"limit":{"type":"integer","title":"Limit"},"offset":{"type":"integer","title":"Offset"},"items":{"items":{"$ref":"#/components/schemas/CustomerQueryRowResponse"},"type":"array","title":"Items"}},"type":"object","required":["tenant_id","period_start","period_end","limit","offset","items"],"title":"UsageQueriesResponse"},"UsageSummaryResponse":{"properties":{"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"period_start":{"type":"string","format":"date","title":"Period Start"},"period_end":{"type":"string","format":"date","title":"Period End"},"total_kt":{"type":"integer","title":"Total Kt"},"by_operation":{"items":{"$ref":"#/components/schemas/UsageOperationBreakdown"},"type":"array","title":"By Operation"}},"type":"object","required":["tenant_id","period_start","period_end","total_kt","by_operation"],"title":"UsageSummaryResponse","description":"Customer kT summary for ``/v1/enterprise/admin/usage/summary``."},"UserMemoryDumpResponse":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"expertise_domains":{"items":{"type":"object"},"type":"array","title":"Expertise Domains"},"effective_role_signal":{"type":"object","title":"Effective Role Signal"},"interaction_preferences":{"type":"object","title":"Interaction Preferences"},"personal_supersession_events":{"items":{"type":"object"},"type":"array","title":"Personal Supersession Events"},"engagement_state":{"type":"object","title":"Engagement State"},"team_collaboration_graph":{"type":"object","title":"Team Collaboration Graph"},"personalization_paused":{"type":"boolean","title":"Personalization Paused"},"memory_building_stopped":{"type":"boolean","title":"Memory Building Stopped"},"memory_purged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Memory Purged At"},"last_edit_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edit At"},"last_reconciliation_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Reconciliation At"},"schema_version":{"type":"integer","title":"Schema Version"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"}},"type":"object","required":["user_id","tenant_id","email","display_name","expertise_domains","effective_role_signal","interaction_preferences","personal_supersession_events","engagement_state","team_collaboration_graph","personalization_paused","memory_building_stopped","memory_purged_at","last_edit_at","last_reconciliation_at","schema_version","created_at","updated_at"],"title":"UserMemoryDumpResponse","description":"Full UserMemory dump for one (tenant, user) pair — operator view."},"UserMemorySummaryResponse":{"properties":{"user_id":{"type":"string","format":"uuid","title":"User Id"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"expertise_domain_count":{"type":"integer","title":"Expertise Domain Count"},"last_edit_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Edit At"},"memory_building_stopped":{"type":"boolean","title":"Memory Building Stopped"},"personalization_paused":{"type":"boolean","title":"Personalization Paused"},"memory_purged_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Memory Purged At"}},"type":"object","required":["user_id","email","display_name","expertise_domain_count","last_edit_at","memory_building_stopped","personalization_paused","memory_purged_at"],"title":"UserMemorySummaryResponse","description":"One row in the tenant user-memory list view."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebhookCreatedResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"provider":{"type":"string","title":"Provider"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"last_received_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Received At"},"secret_once":{"type":"string","title":"Secret Once"}},"type":"object","required":["id","name","provider","enabled","created_at","secret_once"],"title":"WebhookCreatedResponse"},"WebhookDeliveryResponse":{"properties":{"providers":{"items":{"$ref":"#/components/schemas/WebhookProviderRowResponse"},"type":"array","title":"Providers"}},"type":"object","required":["providers"],"title":"WebhookDeliveryResponse"},"WebhookListResponse":{"properties":{"webhooks":{"items":{"$ref":"#/components/schemas/WebhookSummary"},"type":"array","title":"Webhooks"}},"type":"object","required":["webhooks"],"title":"WebhookListResponse"},"WebhookProviderRowResponse":{"properties":{"provider":{"type":"string","title":"Provider"},"last_event_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Event At"},"age_seconds":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Age Seconds"},"threshold_seconds":{"type":"integer","title":"Threshold Seconds"}},"type":"object","required":["provider","threshold_seconds"],"title":"WebhookProviderRowResponse"},"WebhookSummary":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"provider":{"type":"string","title":"Provider"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"last_received_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Received At"}},"type":"object","required":["id","name","provider","enabled","created_at","updated_at"],"title":"WebhookSummary","description":"Returned by list + detail GETs — never carries the secret."},"WebhookUpdatedResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"provider":{"type":"string","title":"Provider"},"enabled":{"type":"boolean","title":"Enabled"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"last_received_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Received At"},"secret_once":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Secret Once"}},"type":"object","required":["id","name","provider","enabled","created_at","updated_at"],"title":"WebhookUpdatedResponse"},"WithdrawContentRequest":{"properties":{"reason":{"type":"string","maxLength":1000,"minLength":1,"title":"Reason"},"dmca":{"type":"boolean","title":"Dmca","description":"True if this is a DMCA/legal request","default":false}},"type":"object","required":["reason"],"title":"WithdrawContentRequest","description":"Request body for content withdrawal."},"WorkspacePatchRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":255,"minLength":1},{"type":"null"}],"title":"Name"},"settings":{"anyOf":[{"$ref":"#/components/schemas/WorkspaceSettingsUpdate"},{"type":"null"}]}},"additionalProperties":false,"type":"object","title":"WorkspacePatchRequest","description":"PATCH /v1/enterprise/workspace request body.\n\nAll fields optional — the action applies a partial update and records\nan audit diff of only the fields that actually changed."},"WorkspaceResponse":{"properties":{"id":{"type":"string","title":"Id"},"name":{"type":"string","title":"Name"},"email_domain":{"type":"string","title":"Email Domain"},"account_type":{"type":"string","enum":["personal","company"],"title":"Account Type"},"status":{"type":"string","title":"Status"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"trial_start":{"type":"string","format":"date-time","title":"Trial Start"},"trial_ends_at":{"type":"string","format":"date-time","title":"Trial Ends At"},"member_count":{"type":"integer","title":"Member Count"},"settings":{"$ref":"#/components/schemas/WorkspaceSettingsModel"}},"type":"object","required":["id","name","email_domain","account_type","status","created_at","trial_start","trial_ends_at","member_count","settings"],"title":"WorkspaceResponse","description":"GET /v1/enterprise/workspace response."},"WorkspaceSettingsModel":{"properties":{"allow_members_invite":{"type":"boolean","title":"Allow Members Invite"},"allow_members_connect":{"type":"boolean","title":"Allow Members Connect"}},"type":"object","required":["allow_members_invite","allow_members_connect"],"title":"WorkspaceSettingsModel","description":"Nested settings payload on the workspace response.\n\nThe two flags extend invite-creation and connector-OAuth to the\n\"member\" role (admin/manager are always allowed)."},"WorkspaceSettingsUpdate":{"properties":{"allow_members_invite":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Members Invite"},"allow_members_connect":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Members Connect"}},"additionalProperties":false,"type":"object","title":"WorkspaceSettingsUpdate","description":"Nested settings block on PATCH requests.\n\nFields omitted from the payload are left untouched. Explicit ``false``\nor ``true`` overwrites the current value."},"_AuditEntry":{"properties":{"id":{"type":"string","title":"Id"},"memory_artifact":{"type":"string","title":"Memory Artifact"},"operation_type":{"type":"string","title":"Operation Type"},"field_path":{"type":"string","title":"Field Path"},"source_event_type":{"type":"string","title":"Source Event Type"},"applied_at":{"type":"string","title":"Applied At"},"applied_by":{"type":"string","title":"Applied By"},"reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reasoning"}},"type":"object","required":["id","memory_artifact","operation_type","field_path","source_event_type","applied_at","applied_by","reasoning"],"title":"_AuditEntry"},"_EquivalentRemainingShape":{"properties":{"fast":{"type":"integer","title":"Fast"},"standard":{"type":"integer","title":"Standard"},"deep":{"type":"integer","title":"Deep"}},"type":"object","required":["fast","standard","deep"],"title":"_EquivalentRemainingShape"},"_QueryCountsTodayShape":{"properties":{"fast":{"type":"integer","title":"Fast"},"standard":{"type":"integer","title":"Standard"},"deep":{"type":"integer","title":"Deep"}},"type":"object","required":["fast","standard","deep"],"title":"_QueryCountsTodayShape"},"_QueryKtCostShape":{"properties":{"fast":{"type":"integer","title":"Fast","example":1500},"standard":{"type":"integer","title":"Standard","example":12500},"deep":{"type":"integer","title":"Deep","example":25000}},"type":"object","required":["fast","standard","deep"],"title":"_QueryKtCostShape"},"_RejectionRow":{"properties":{"piece_id":{"type":"string","title":"Piece Id"},"tenant_id":{"type":"string","title":"Tenant Id"},"title":{"type":"string","title":"Title"},"reason_code":{"type":"string","title":"Reason Code"},"redacted_secrets_count":{"type":"integer","title":"Redacted Secrets Count"},"flagged_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flagged At"},"overridden":{"type":"boolean","title":"Overridden"}},"type":"object","required":["piece_id","tenant_id","title","reason_code","redacted_secrets_count","flagged_at","overridden"],"title":"_RejectionRow"},"_TenantMemorySliceForUser":{"properties":{"company_description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Company Description"},"business_model":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Business Model"},"top_customer_segments":{"items":{"type":"string"},"type":"array","title":"Top Customer Segments"}},"type":"object","title":"_TenantMemorySliceForUser","description":"The slice of tenant memory the user is allowed to see. Framed as\n\"what your company's brain knows\" — transparency, not surveillance."},"_UserMemoryExpertise":{"properties":{"domain":{"type":"string","title":"Domain"},"confidence_band":{"type":"string","title":"Confidence Band"}},"type":"object","required":["domain","confidence_band"],"title":"_UserMemoryExpertise","description":"Qualitative label for one expertise domain. Confidence is\nsurfaced as a band, NOT a raw float — the privacy contract is that\nuser-facing surfaces never expose raw classifier scores."},"app__domains__admin__presentation__tenants_billing_routes__AuditEntryResponse":{"properties":{"occurred_at":{"type":"string","format":"date-time","title":"Occurred At"},"source":{"type":"string","title":"Source"},"action":{"type":"string","title":"Action"},"actor":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"metadata":{"type":"object","title":"Metadata"}},"type":"object","required":["occurred_at","source","action"],"title":"AuditEntryResponse","description":"One entry in a tenant's combined audit history.\n\nSources:\n  * ``state_machine`` — tenant_status_history rows from D1 transitions\n  * ``admin``         — admin_audit_log rows from operator actions\n  * ``webhook``       — processed_events rows (Stripe webhook deliveries)"},"app__domains__admin__presentation__v09_backoffice_routes__BackofficeOverrideAck":{"properties":{"ok":{"type":"boolean","title":"Ok"},"audit_log_id":{"type":"string","format":"uuid","title":"Audit Log Id"},"applied_at":{"type":"string","format":"date-time","title":"Applied At"},"effect":{"type":"object","title":"Effect"}},"additionalProperties":false,"type":"object","required":["ok","audit_log_id","applied_at","effect"],"title":"BackofficeOverrideAck","description":"Response shape for every successful override (Lock-defined).\n\nMatches ``v09-openapi.yaml#/components/schemas/BackofficeOverrideAck``\nplus an ``effect`` block carrying the structured before/after diff for\noperator review (Lock-additive — backwards-compatible since the\nexisting required fields are unchanged)."},"app__domains__enterprise__billing__presentation__pack_backoffice_routes__BackofficeOverrideAck":{"properties":{"ok":{"type":"boolean","title":"Ok"},"audit_log_id":{"type":"string","title":"Audit Log Id"},"applied_at":{"type":"string","format":"date-time","title":"Applied At"}},"type":"object","required":["ok","audit_log_id","applied_at"],"title":"BackofficeOverrideAck"},"app__domains__enterprise__billing__presentation__payg_backoffice_routes__BackofficeOverrideAck":{"properties":{"ok":{"type":"boolean","title":"Ok"},"audit_log_id":{"type":"string","title":"Audit Log Id"},"applied_at":{"type":"string","format":"date-time","title":"Applied At"}},"type":"object","required":["ok","audit_log_id","applied_at"],"title":"BackofficeOverrideAck"},"app__domains__memory__presentation__admin_routes__AuditEntryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"tenant_id":{"type":"string","format":"uuid","title":"Tenant Id"},"user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"User Id"},"memory_artifact":{"type":"string","title":"Memory Artifact"},"operation_type":{"type":"string","title":"Operation Type"},"field_path":{"type":"string","title":"Field Path"},"source_event_type":{"type":"string","title":"Source Event Type"},"source_event_id":{"type":"string","format":"uuid","title":"Source Event Id"},"source_content_piece_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Source Content Piece Id"},"classifier_confidence":{"type":"number","title":"Classifier Confidence"},"previous_value":{"anyOf":[{},{"type":"null"}],"title":"Previous Value"},"new_value":{"title":"New Value"},"applied_at":{"type":"string","format":"date-time","title":"Applied At"},"applied_by":{"type":"string","title":"Applied By"},"operator_user_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Operator User Id"},"reasoning":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reasoning"}},"type":"object","required":["id","tenant_id","user_id","memory_artifact","operation_type","field_path","source_event_type","source_event_id","source_content_piece_id","classifier_confidence","previous_value","new_value","applied_at","applied_by","operator_user_id","reasoning"],"title":"AuditEntryResponse"}},"securitySchemes":{"APIKeyHeader":{"type":"apiKey","in":"header","name":"X-Admin-Key"}}}}